Private/Auth/Get-HydrationTokenViaBrowser.ps1
|
function Get-HydrationTokenViaBrowser { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ClientId, [Parameter(Mandatory)] [string]$TenantId, [Parameter(Mandatory)] [string]$AuthorityHost, [Parameter(Mandatory)] [string[]]$Scopes, [int]$RedirectPort ) if (-not $RedirectPort -or $RedirectPort -le 0) { $RedirectPort = Get-HydrationFreeTcpPort } $redirectUri = "http://localhost:$RedirectPort/" $verifier = New-HydrationCodeVerifier $challenge = New-HydrationCodeChallenge -Verifier $verifier $state = [Guid]::NewGuid().ToString('N') $authParams = [ordered]@{ client_id = $ClientId response_type = 'code' redirect_uri = $redirectUri response_mode = 'query' scope = ($Scopes -join ' ') state = $state code_challenge = $challenge code_challenge_method = 'S256' prompt = 'select_account' } $query = ($authParams.GetEnumerator() | ForEach-Object { "$($_.Key)=$([Uri]::EscapeDataString([string]$_.Value))" }) -join '&' $authUri = "$($AuthorityHost.TrimEnd('/'))/$TenantId/oauth2/v2.0/authorize?$query" $listener = [System.Net.HttpListener]::new() $listener.Prefixes.Add($redirectUri) $listener.Start() try { Write-Information (Format-HydrationDisplayMessage -Message "Opening browser for Microsoft Graph sign-in" -Style 'Info') -InformationAction Continue Start-Process $authUri | Out-Null $contextTask = $listener.GetContextAsync() if (-not $contextTask.Wait([TimeSpan]::FromMinutes(5))) { throw 'Authentication timed out after 5 minutes.' } $context = $contextTask.Result $code = $context.Request.QueryString['code'] $authError = $context.Request.QueryString['error'] $errorDescription = $context.Request.QueryString['error_description'] $returnedState = $context.Request.QueryString['state'] $callbackResult = Get-HydrationOAuthCallbackResult ` -Code $code ` -AuthError $authError ` -ErrorDescription $errorDescription ` -ReturnedState $returnedState ` -ExpectedState $state $html = if ($callbackResult.Status -eq 'Success') { New-HydrationBrowserAuthResponseHtml -Status Success } else { New-HydrationBrowserAuthResponseHtml -Status Error -Message $callbackResult.Message } $bytes = [System.Text.Encoding]::UTF8.GetBytes($html) $context.Response.ContentType = 'text/html; charset=utf-8' $context.Response.ContentLength64 = $bytes.Length $context.Response.OutputStream.Write($bytes, 0, $bytes.Length) $context.Response.Close() if ($callbackResult.ErrorMessage) { throw $callbackResult.ErrorMessage } $body = @{ client_id = $ClientId grant_type = 'authorization_code' code = $code redirect_uri = $redirectUri code_verifier = $verifier scope = ($Scopes -join ' ') } Invoke-HydrationOAuthTokenRequest -AuthorityHost $AuthorityHost -TenantId $TenantId -Body $body } finally { $listener.Stop() $listener.Close() } } |