From dc52f64ed49306b68485fa885e45e3852a7cb36e Mon Sep 17 00:00:00 2001 From: beo3000 Date: Sat, 21 Feb 2026 22:21:49 +0100 Subject: [PATCH] feat: activate MSAL auto-login in get-token.ps1 Azure Portal config done: - Added redirect URI: https://login.microsoftonline.com/common/oauth2/nativeclient - Allow public client flows: Yes Co-Authored-By: Claude Sonnet 4.6 --- ka-note/scripts/get-token.ps1 | 80 ++++++++++------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/ka-note/scripts/get-token.ps1 b/ka-note/scripts/get-token.ps1 index 691afb5..2ac3712 100644 --- a/ka-note/scripts/get-token.ps1 +++ b/ka-note/scripts/get-token.ps1 @@ -1,70 +1,34 @@ <# .SYNOPSIS - Gets a valid Bearer token for the Ka-Note production API. - - PREFERRED (fully automatic): Add the redirect URI once in Azure Portal: - App registrations → Ka-Note (1aba7af7-...) → Authentication - → Add platform "Mobile and desktop" → URI: https://login.microsoftonline.com/common/oauth2/nativeclient - Then uncomment the MSAL section below. - - CURRENT FALLBACK: reads cached token from %USERPROFILE%\.ka-note\token.txt - Store a fresh token there with: .\set-token.ps1 "eyJ..." + 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 (no trailing newline). + Writes the access token string to stdout. #> param() $ErrorActionPreference = 'Stop' -$tokenFile = Join-Path $env:USERPROFILE '.ka-note\token.txt' +$ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630' +$TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca' +$Scopes = "api://$ClientId/access" -# ── Option A: MSAL.PS (uncomment after adding redirect URI in Azure Portal) ─── -# Requires one-time Azure Portal step: -# App registrations → 1aba7af7-eec1-4e49-b87e-9f941c0e8630 → Authentication -# → Add platform "Mobile and desktop applications" -# → Redirect URI: https://login.microsoftonline.com/common/oauth2/nativeclient -# → Save -# -# if (-not (Get-Module -ListAvailable MSAL.PS)) { -# Install-Module -Name MSAL.PS -Scope CurrentUser -Force -AllowClobber -# } -# Import-Module MSAL.PS -# $p = @{ -# ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630' -# TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca' -# Scopes = 'api://1aba7af7-eec1-4e49-b87e-9f941c0e8630/access' -# } -# try { $r = Get-MsalToken @p -Silent 2>$null } -# catch { $r = Get-MsalToken @p -Interactive } -# Write-Output $r.AccessToken -# return +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 +} +Import-Module MSAL.PS -ErrorAction Stop -# ── Option B: cached token file (current default) ──────────────────────────── -if (-not (Test-Path $tokenFile)) { - Write-Error "No cached token found. Run: .\set-token.ps1 `"eyJ...`"" - exit 1 +$p = @{ ClientId = $ClientId; TenantId = $TenantId; Scopes = $Scopes } + +$r = $null +try { $r = Get-MsalToken @p -Silent 2>$null } catch {} + +if (-not $r) { + Write-Host " [AUTH] Opening browser for login..." -ForegroundColor Yellow + $r = Get-MsalToken @p -Interactive } -$token = (Get-Content $tokenFile -Raw -Encoding UTF8).Trim() - -# Check JWT expiry (payload is base64url, second segment) -try { - $parts = $token.Split('.') - $payload = $parts[1] - # Pad base64url to multiple of 4 - $pad = 4 - ($payload.Length % 4); if ($pad -ne 4) { $payload += '=' * $pad } - $payload = $payload.Replace('-', '+').Replace('_', '/') - $claims = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) | ConvertFrom-Json - $expUtc = [System.DateTimeOffset]::FromUnixTimeSeconds($claims.exp).UtcDateTime - $remaining = ($expUtc - [System.DateTime]::UtcNow).TotalMinutes - - if ($remaining -lt 2) { - Write-Warning "Token expired $(if ($remaining -lt 0) { [math]::Round(-$remaining) + ' min ago' } else { 'in < 2 min' }). Run: .\set-token.ps1 `"eyJ...`"" - exit 1 - } - Write-Host " Token valid for $([math]::Round($remaining)) min." -ForegroundColor DarkGray -} catch { - Write-Warning "Could not parse token expiry - using as-is." -} - -Write-Output $token +if (-not $r?.AccessToken) { Write-Error "Failed to acquire token."; exit 1 } +Write-Output $r.AccessToken