diff --git a/src/private/AuthWebServer.ps1 b/src/private/AuthWebServer.ps1 index 280d31b..d6ac5ee 100644 --- a/src/private/AuthWebServer.ps1 +++ b/src/private/AuthWebServer.ps1 @@ -13,28 +13,52 @@ Function Wait-GoogleAuthApiToken $HttpListener.Start() $authCodeReceived = $False - + while ($HttpListener.IsListening -and -not $authCodeReceived) { - $HttpContext = $HttpListener.GetContext() - $HttpRequest = $HttpContext.Request - $Query = $HttpRequest.QueryString + # Use async method with timeout to allow for cancellation + $contextTask = $HttpListener.GetContextAsync() + + # Wait for either a request or cancellation (check every 500ms) + while (-not $contextTask.IsCompleted) { + Start-Sleep -Milliseconds 500 + + # Check if user pressed Ctrl+C by testing if we can write to console + try { + [Console]::TreatControlCAsInput = $false + if ([Console]::KeyAvailable) { + $key = [Console]::ReadKey($true) + if ($key.Key -eq 'C' -and $key.Modifiers -eq 'Control') { + Write-Information "`nCancellation requested. Stopping auth server..." + return $null + } + } + } + catch { + # Ignore console access errors + } + } + + if ($contextTask.IsCompleted) { + $HttpContext = $contextTask.Result + $HttpRequest = $HttpContext.Request + $Query = $HttpRequest.QueryString - if ($null -ne $Query["code"]) { - $authCode = $Query["code"] - Write-Output $authCode - Write-Verbose "Received auth-code: $authCode" - $authCodeReceived = $true + if ($null -ne $Query["code"]) { + $authCode = $Query["code"] + Write-Output $authCode + Write-Verbose "Received auth-code: $authCode" + $authCodeReceived = $true - # Send "Thanks!" - $buffer = [System.Text.Encoding]::UTF8.GetBytes("Good Job! Successfully authorized PSBlogger. You can close this browser window now.") - $response = $HttpContext.Response - $response.ContentLength64 = $buffer.Length - $output = $response.OutputStream; - $output.Write($buffer,0,$buffer.Length) - $output.Close() | Write-Verbose - } + # Send "Thanks!" + $buffer = [System.Text.Encoding]::UTF8.GetBytes("Good Job! Successfully authorized PSBlogger. You can close this browser window now.") + $response = $HttpContext.Response + $response.ContentLength64 = $buffer.Length + $output = $response.OutputStream; + $output.Write($buffer,0,$buffer.Length) + $output.Close() | Write-Verbose + } + } } - Write-Verbose "Stopping HttpListener." $HttpListener.Stop() Write-Verbose "Stopped HttpListener." @@ -42,7 +66,7 @@ Function Wait-GoogleAuthApiToken catch { # TODO: Catch Permission denied error and warn about running from an elevated prompt # or add Requires -Administrator - Write-Error $_.ToString() + Write-Error $_.ToString() -ErrorAction Stop } finally { if ($null -ne $HttpListener) { diff --git a/src/public/Initialize-Blogger.ps1 b/src/public/Initialize-Blogger.ps1 index 0983315..25607ba 100644 --- a/src/public/Initialize-Blogger.ps1 +++ b/src/public/Initialize-Blogger.ps1 @@ -6,14 +6,14 @@ This prepares your system to use Pandoc + Blogger together by obtaining an authtoken that is authorized to communicate with blogger. -.PARAMETER clientId +.PARAMETER ClientId Google API Client ID. This currently defaults to the one I use, but you will need to create your own until the Google Application is published and verified -.PARAMETER clientSecret +.PARAMETER ClientSecret Google API Client Secret. A default value is provided, but you can provide your own if you don't trust me. -.PARAMETER redirectUri +.PARAMETER RedirectUri The oAuth redirect URL specifed in the Google API Consent Form. .EXAMPLE @@ -23,18 +23,33 @@ Initiate a login flow with Google #> Function Initialize-Blogger { + [CmdletBinding()] Param( [Parameter(HelpMessage = "Google API ClientId")] - [string]$clientId = "<>", + [string]$ClientId = "<>", [Parameter(HelpMessage = "Google API Client Secret")] - [string]$clientSecret = "<>", + [string]$ClientSecret = "<>", [Parameter(HelpMessage = "Redirect Uri specified in Google API Consent Form")] - [string]$redirectUri = "http://localhost/oauth2callback" + [string]$RedirectUri = "http://localhost/oauth2callback" ) $ErrorActionPreference = 'Stop' + if ($env:PSBLOGGER_CLIENT_ID -and !$PSBoundParameters.ContainsKey("ClientId")) + { + Write-Verbose "Using environment variable PSBLOGGER_CLIENT_ID for ClientId" + $ClientId = $env:PSBLOGGER_CLIENT_ID + } + if ($env:PSBLOGGER_CLIENT_SECRET -and !$PSBoundParameters.ContainsKey("ClientSecret")) + { + Write-Verbose "Using environment variable PSBLOGGER_CLIENT_SECRET for ClientSecret" + $ClientSecret = $env:PSBLOGGER_CLIENT_SECRET + } + if ($ClientId -eq "<>" -or $ClientSecret -eq "<>") { + Write-Error "See contribution guide for how to set up your own Google API for local development." + return + } Write-Information "Let's get an auth-code." @@ -42,26 +57,30 @@ Function Initialize-Blogger { $scope = @( "https://www.googleapis.com/auth/blogger" "https://www.googleapis.com/auth/drive.file" - ) -join " " - $url = "https://accounts.google.com/o/oauth2/auth?client_id=$clientId&scope=$scope&response_type=code&redirect_uri=$redirectUri&access_type=offline&approval_prompt=force" + $url = "https://accounts.google.com/o/oauth2/auth?client_id=$ClientId&scope=$scope&response_type=code&redirect_uri=$RedirectUri&access_type=offline&approval_prompt=force" Start-Process $url $code = Wait-GoogleAuthApiToken + if ($null -eq $code) { + Write-Information "Authentication cancelled by user." + return + } + Write-Information "Sucessfully obtained auth-code!" # trade the auth-code for an token that has a short-lived expiry - $expiringToken = Get-GoogleAccessToken -clientId $clientId -clientSecret $clientSecret -redirectUri $redirectUri -code $code + $expiringToken = Get-GoogleAccessToken -clientId $ClientId -clientSecret $ClientSecret -redirectUri $RedirectUri -code $code # use the refresh-token to get an updated access-token - $token = Update-GoogleAccessToken -clientId $clientId -clientSecret $clientSecret -refreshToken $expiringToken.refresh_token + $token = Update-GoogleAccessToken -clientId $ClientId -clientSecret $ClientSecret -refreshToken $expiringToken.refresh_token # save the token in the credential_cache.json - Set-CredentialCache -clientId $clientId -clientSecret $clientSecret -refreshToken $expiringToken -token $token - + Set-CredentialCache -clientId $ClientId -clientSecret $ClientSecret -refreshToken $expiringToken -token $token + Write-Information "Awesome. We're all set." } \ No newline at end of file diff --git a/src/tests/Initialize-Blogger.Tests.ps1 b/src/tests/Initialize-Blogger.Tests.ps1 index 60dad35..55c9714 100644 --- a/src/tests/Initialize-Blogger.Tests.ps1 +++ b/src/tests/Initialize-Blogger.Tests.ps1 @@ -32,10 +32,13 @@ Describe "Initialize-Blogger" { # arrange $credentialCache = "TestDrive:\credentialcache.json" $BloggerSession.CredentialCache = $credentialCache - + $initArgs = @{ + ClientId = "dummy" + ClientSecret = "dummy" + } # act - Initialize-Blogger + Initialize-Blogger @initArgs # assert $credentials = Get-Content -Path $credentialCache | ConvertFrom-Json @@ -53,9 +56,14 @@ Describe "Initialize-Blogger" { $BloggerSession.AccessToken = "invalid" $BloggerSession.RefreshToken = "invalid" + $initArgs = @{ + ClientId = "dummy" + ClientSecret = "dummy" + } + # act - Initialize-Blogger - + Initialize-Blogger @initArgs + # assert $BloggerSession.AccessToken | Should -Be $null $BloggerSession.RefreshToken | Should -Be $null