From c28bd34a1afef2d6434db6ea79033d317a5114e4 Mon Sep 17 00:00:00 2001 From: beo3000 Date: Sat, 21 Feb 2026 22:31:03 +0100 Subject: [PATCH] fix: persistent token cache in get-token.ps1 - Fast path: reads from ~/.ka-note/token.txt if not expired (no MSAL needed) - On expiry: MSAL silent refresh with login hint - Fallback: interactive browser login (~once per 90 days) - Auto-saves every acquired token back to cache file Co-Authored-By: Claude Sonnet 4.6 --- ka-note/scripts/get-token.ps1 | 66 ++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/ka-note/scripts/get-token.ps1 b/ka-note/scripts/get-token.ps1 index 2ac3712..9faad65 100644 --- a/ka-note/scripts/get-token.ps1 +++ b/ka-note/scripts/get-token.ps1 @@ -1,34 +1,68 @@ <# .SYNOPSIS - Gets a valid Bearer token for the Ka-Note production API via MSAL.PS. - On first run (or after token cache expires): opens browser popup for login. - Subsequent runs: silent refresh via cached refresh token. - -.OUTPUTS - Writes the access token string to stdout. + Gets a valid Bearer token for the Ka-Note production API. + - Reads from ~/.ka-note/token.txt if not expired (fast path) + - On expiry: MSAL silent refresh (no browser needed if refresh token valid) + - Fallback: interactive browser login (once per ~90 days) + - Saves every acquired token back to the cache file automatically #> param() $ErrorActionPreference = 'Stop' -$ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630' -$TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca' -$Scopes = "api://$ClientId/access" +$ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630' +$TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca' +$Scopes = "api://$ClientId/access" +$CacheFile = Join-Path $env:USERPROFILE '.ka-note\token.txt' +$LoginHint = 'C.KAUER@KRAH-GRUPPE.DE' -if (-not (Get-Module -ListAvailable -Name 'MSAL.PS')) { - Write-Host " [INFO] Installing MSAL.PS..." -ForegroundColor DarkGray - Install-Module -Name 'MSAL.PS' -Scope CurrentUser -Force -AllowClobber +function Get-JwtExpiry([string]$jwt) { + try { + $seg = $jwt.Split('.')[1] + $pad = 4 - ($seg.Length % 4); if ($pad -ne 4) { $seg += '=' * $pad } + $seg = $seg.Replace('-','+').Replace('_','/') + $c = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($seg)) | ConvertFrom-Json + return [System.DateTimeOffset]::FromUnixTimeSeconds($c.exp).UtcDateTime + } catch { return [DateTime]::MinValue } +} + +function Save-Token([string]$token) { + $dir = Split-Path $CacheFile + if (-not (Test-Path $dir)) { New-Item -ItemType Directory $dir | Out-Null } + $token | Set-Content $CacheFile -Encoding UTF8 -NoNewline +} + +# ── 1. Try cached file ──────────────────────────────────────────────────────── +if (Test-Path $CacheFile) { + $cached = (Get-Content $CacheFile -Raw -Encoding UTF8).Trim() + $exp = Get-JwtExpiry $cached + $remaining = ($exp - [DateTime]::UtcNow).TotalMinutes + if ($remaining -gt 2) { + Write-Host " Token valid for $([math]::Round($remaining)) min (cached)." -ForegroundColor DarkGray + Write-Output $cached + return + } + Write-Host " Cached token expired. Refreshing via MSAL..." -ForegroundColor DarkGray +} + +# ── 2. MSAL refresh / interactive ──────────────────────────────────────────── +if (-not (Get-Module -ListAvailable MSAL.PS)) { + Write-Host " Installing MSAL.PS..." -ForegroundColor DarkGray + Install-Module -Name MSAL.PS -Scope CurrentUser -Force -AllowClobber } Import-Module MSAL.PS -ErrorAction Stop $p = @{ ClientId = $ClientId; TenantId = $TenantId; Scopes = $Scopes } $r = $null -try { $r = Get-MsalToken @p -Silent 2>$null } catch {} +try { $r = Get-MsalToken @p -Silent -LoginHint $LoginHint 2>$null } catch {} -if (-not $r) { +if (-not $r -or -not $r.AccessToken) { Write-Host " [AUTH] Opening browser for login..." -ForegroundColor Yellow - $r = Get-MsalToken @p -Interactive + $r = Get-MsalToken @p -Interactive -LoginHint $LoginHint } -if (-not $r?.AccessToken) { Write-Error "Failed to acquire token."; exit 1 } +if (-not $r -or -not $r.AccessToken) { Write-Error "Failed to acquire token."; exit 1 } + +Save-Token $r.AccessToken +Write-Host " Token acquired and cached." -ForegroundColor Green Write-Output $r.AccessToken