Requested: dev-token.sh/.ps1 tokens should last 3h by default, with an argument to override the duration. Server-verified finding: STALWART_RECOVERY_ADMIN (the break-glass account docker-compose.yml and the scripts used) always issues OAuth tokens with a fixed 1h expiry, regardless of the server's configured accessTokenExpiry — confirmed against the live container, including after changing the setting and restarting. Confirmed x:ApiKey objects, by contrast, support an arbitrary expiresAt set per request, and their secret works directly as a bearer token. Add scripts/dev-server-init.sh (+ .ps1): a one-time, idempotent setup step that completes the server's bootstrap wizard (default domain, no TLS certificate request), creates a real "devadmin" admin account, and sets the server's default OAuth token lifetime to 3h. Rework dev-token.sh/.ps1 to authenticate as devadmin and create an x:ApiKey with a caller-supplied expiry (`dev-token.sh 1800` for 30 minutes, defaults to 10800s/3h) instead of running the OAuth PKCE flow against the recovery account. Verified end-to-end against a fresh container, including a real browser session against the running WebUI. Also includes an incidental package-lock.json sync (was still pinned to v1.0.8 / stale dependency ranges from before the upstream merge).
72 lines
2.8 KiB
PowerShell
72 lines
2.8 KiB
PowerShell
#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."
|