#Requires -Version 5.1 <# .SYNOPSIS Generates a fresh access token from the local Stalwart dev container and writes it to .env.development.local (gitignored). .DESCRIPTION Local development only. Requires scripts/dev-server-init.ps1 to have been run once first (creates the "devadmin" account this script authenticates as — the STALWART_RECOVERY_ADMIN account is break-glass only and always issues fixed 1h tokens regardless of server config, so it can't honor a custom duration). See DEVELOPMENT.md for the full workflow. Non-Windows shells (and AI agents without PowerShell) can use scripts/dev-token.sh instead. .PARAMETER DurationSeconds How long the token should stay valid, in seconds. Defaults to 3 hours (10800), matching the server default set by dev-server-init.ps1. .EXAMPLE ./dev-token.ps1 # 3 hour token .EXAMPLE ./dev-token.ps1 -DurationSeconds 1800 # 30 minute token #> param( [int]$DurationSeconds = 10800, [string]$ApiBaseUrl = "http://localhost:8080" ) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $DevAdminAccount = "devadmin@example.org" $DevAdminSecret = "DevAdminPass123!" $creds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($DevAdminAccount):$($DevAdminSecret)")) $authHeader = @{ Authorization = "Basic $creds" } try { $session = Invoke-RestMethod -Uri "$ApiBaseUrl/jmap/session" -Headers $authHeader -TimeoutSec 15 } catch { throw "Could not reach $ApiBaseUrl as $DevAdminAccount. Is the server running ('npm run dev:server') and initialized ('scripts/dev-server-init.ps1')?" } $accountId = $session.primaryAccounts.'urn:stalwart:jmap' $expiresAt = [DateTime]::UtcNow.AddSeconds($DurationSeconds).ToString("yyyy-MM-ddTHH:mm:ssZ") $request = @{ using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap") methodCalls = @(, @("x:ApiKey/set", @{ accountId = $accountId create = @{ k1 = @{ description = "dev-token.ps1"; expiresAt = $expiresAt } } }, "0")) } | ConvertTo-Json -Depth 10 -Compress $response = Invoke-RestMethod -Uri "$ApiBaseUrl/jmap/" -Method Post -ContentType "application/json" -Headers $authHeader -Body $request -TimeoutSec 15 $secret = $response.methodResponses[0][1].created.k1.secret if (-not $secret) { throw "Unexpected x:ApiKey/set response: $($response | ConvertTo-Json -Depth 10 -Compress)" } $envPath = Join-Path $root ".env.development.local" @" # Generated by scripts/dev-token.ps1 - gitignored, do not commit. # Empty base URL: API calls stay same-origin and go through the Vite proxy. VITE_API_BASE_URL= VITE_ACCESS_TOKEN=$secret "@ | Set-Content -Path $envPath -Encoding ascii Write-Host "Token written to $envPath (expires $expiresAt, in ${DurationSeconds}s). Restart 'npm run dev' to pick it up."