<# .SYNOPSIS 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" $CacheFile = Join-Path $env:USERPROFILE '.ka-note\token.txt' $LoginHint = 'C.KAUER@KRAH-GRUPPE.DE' 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 -LoginHint $LoginHint 2>$null } catch {} if (-not $r -or -not $r.AccessToken) { Write-Host " [AUTH] Opening browser for login..." -ForegroundColor Yellow $r = Get-MsalToken @p -Interactive -LoginHint $LoginHint } 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