79471029

Date: 2025-02-26 21:13:40
Score: 2.5
Natty:
Report link

I had the same issue in PowerShell.

Full disclosure: I didn't have any luck finding the "proper" way to do this in PowerShell, so I had to hack something out...This is what I have so far. I wouldn't consider this to be the "proper" way, it's just a way that is actually working for me. I borrowed snippets from various examples to kludge this together.

# CookieManager isn't available until Initialization is completed.  So I get it in the Initialization Completed event.
        $coreWebView2Initialized = {
            $script:cookieManager = $web.CoreWebView2.CookieManager;
            $script:cookies = $script:cookieManager.GetCookiesAsync("");
            $script:coreweb2pid =  $web.CoreWebView2.BrowserProcessId;    #this I used later to find out if the webview2 process was closed so I could delete the cache.
        }
        $web.add_CoreWebView2InitializationCompleted($coreWebView2Initialized);


# Once the initial naviation is completed I hook to the GetCookiesAsync method.
        $web_NavigationCompleted = {
            $script:cookies = $script:cookieManager.GetCookiesAsync("");
        }
        $web.add_NavigationCompleted($web_NavigationCompleted)

# With my particular situation, I wanted to deal with MFA/JWT authentication with a 3rd party vendor talking to our MFA provider.  The vendor uses javascript to change pages which dosen't trigger a webview2 event.  I added a javascript observer that watched for the documentElement.innerText for the "You can close" text that the 3rd party provider would return indicating it's ok to close the browser.  Once this text came through I used the webview.postMessage('Close!') to send a message back to my script so it could close the form and cleanup everything.
#  The specific part of this that addressed the getting async cookies part is adding the GetCookiesAsync hookup once the initial page is loaded.  For me, the cookies I wanted were HTTP Only cookies so I had to do it this way to get at them.

   $web_NavigationCompleted = {
        $script:cookies = $script:cookieManager.GetCookiesAsync("");
        $web.CoreWebView2.ExecuteScriptAsync("
            //Setup an observer to watch for time to close the window
            function observerCallback(mutations) {
                if ( (document.documentElement.textContent || document.documentElement.innerText).indexOf('You can close') > -1 ) {
                    //send a Close! message back to webview2 so it can close the window and complete.
                    window.chrome.webview.postMessage('Close!');
                }    
            }    
            const observer = new MutationObserver(observerCallback);
            const targetNode = document.documentElement || document.body;
            const observerconf = { attributes: true, childList: true, subtree: true, characterData: true };
            observer.observe(targetNode, observerconf);
        ");
    }
        $web.add_NavigationCompleted($web_NavigationCompleted)

#   Once the form "Close!" message is generted, the cookie I want should be there.  This is ignoring any of the misc innerText events that happen and just waiting for the "Close!".
#   I grab the specific HTTP Only cookie and return the value.

   $web.add_WebMessageReceived({
        param($WebView2, $message)
 
        if ($message.TryGetWebMessageAsString() -eq 'Close!') {
            $result = ($cookies.Result | Where-Object {$_.name -eq "The_Name_of_the_HTTP_ONLY_Cookie_I_Wanted"}).value
            $web.Dispose()
            # Cleanup cache dir if desired - wait for the webview2 process to close after the dispose(), then you can delete the cache dir.
            if ($Purge_cache) {
                if ($debug) {write-host "form closing webview2 pid "$script:coreweb2pid -ForegroundColor blue} 
                    $timeout = 0
                    try
                    {
                        while ($null -ne [System.Diagnostics.Process]::GetProcessById($script:coreweb2pid)  -and $timeout -le 2000)
                        {
                            if ($debug) {write-host "Waiting for close pid "$script:coreweb2pid -ForegroundColor blue} 
                            Start-Sleep -seconds 1                      
                            $timeout += 10;
                        }
                    }
                    catch { }
                    if ($debug) {write-host "cleaning up old temp folder" -ForegroundColor blue} 
                    
                    $OriginalPref = $ProgressPreference 
                    $ProgressPreference = "SilentlyContinue"
                    $null = remove-item  "$([IO.Path]::Combine( [String[]]([IO.Path]::GetTempPath(), 'MyTempWebview2CacheDir')) )" -Recurse -Force 
                    $ProgressPreference = $originalpref            
            }
            $form.Close()
                
            return $result.tostring()
        }
    })

There's probably a cleaner way to do this. For now, it works. It drove me crazy because if you dig for anything about doing MFA authentication with PowerShell you end up with O365 examples...Like the only thing we'd use PowerShell for is O365? If/when I get my authentication module polished enough to post, I'll add it to GitHub and update this post. I spent a lot of time running around in circles trying to do this in PowerShell, hopefully this removes that barrier for folks.

Note: I've also tried setting incognito mode for the Webview2 browser (many ways). That doesn't work, not so far anyway. I don't like any of this authentication data being written to disk in cache or any other way, I want it to be momentary authorization for the use of the script and then gone...I am continuing to work on making this not cache things, but for now at least I have a path to delete the browser environment cache.

Cheers.

Reasons:
  • Blacklisted phrase (1): Cheers
  • Blacklisted phrase (1.5): any luck
  • Whitelisted phrase (-1): I had the same
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • Low reputation (1):
Posted by: Chris Amendola