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 <noreply@anthropic.com>
This commit is contained in:
beo3000 2026-02-21 22:31:03 +01:00
parent dc52f64ed4
commit c28bd34a1a
1 changed files with 50 additions and 16 deletions

View File

@ -1,11 +1,10 @@
<#
.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'
@ -13,22 +12,57 @@ $ErrorActionPreference = 'Stop'
$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