From 54c3f30ebce672c3cf861f5dad7fdda91f3498d2 Mon Sep 17 00:00:00 2001
From: bryan cook <3217452+bryanbcook@users.noreply.github.com>
Date: Wed, 30 Jul 2025 17:21:36 -0400
Subject: [PATCH 1/3] Add cancellation to auth flow #44
---
src/private/AuthWebServer.ps1 | 62 +++++++++++++++++++++----------
src/public/Initialize-Blogger.ps1 | 29 +++++++++------
2 files changed, 60 insertions(+), 31 deletions(-)
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..3f7a79f 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,15 +23,16 @@ 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'
@@ -42,26 +43,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
From ae869486177c05ed75774749cb53afb8710270b6 Mon Sep 17 00:00:00 2001
From: bryan cook <3217452+bryanbcook@users.noreply.github.com>
Date: Wed, 30 Jul 2025 17:29:08 -0400
Subject: [PATCH 2/3] use environment variables when running locally
---
src/public/Initialize-Blogger.ps1 | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/public/Initialize-Blogger.ps1 b/src/public/Initialize-Blogger.ps1
index 3f7a79f..25607ba 100644
--- a/src/public/Initialize-Blogger.ps1
+++ b/src/public/Initialize-Blogger.ps1
@@ -36,6 +36,20 @@ Function Initialize-Blogger {
)
$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."
From ab73d8e56beba4de1aab0ee27b67d71240db26fe Mon Sep 17 00:00:00 2001
From: bryan cook <3217452+bryanbcook@users.noreply.github.com>
Date: Sun, 3 Aug 2025 15:44:58 -0400
Subject: [PATCH 3/3] fixes for tests
---
src/tests/Initialize-Blogger.Tests.ps1 | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
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