feat(dev): switch dev tokens to a real admin account with configurable duration
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).
This commit is contained in:
+48
-18
@@ -39,27 +39,55 @@ npm run dev:server:down # stop it (add `-v` via `docker compose down -v` to al
|
||||
The server takes a couple of seconds to come up; `docker compose logs stalwart`
|
||||
will show `Network listener started ... localPort = 8080` once it's ready.
|
||||
|
||||
## 2. Get an access token
|
||||
## 2. Initialize the server (first time only)
|
||||
|
||||
The WebUI normally authenticates through an OAuth flow in the browser, but
|
||||
for local development it's simpler to skip that and use a bearer token
|
||||
directly via `VITE_ACCESS_TOKEN` (see `.env.development`).
|
||||
The container starts in Stalwart's bootstrap mode, which only allows
|
||||
signing in as the break-glass `STALWART_RECOVERY_ADMIN` account — real
|
||||
accounts and most settings aren't usable yet. Run once per fresh volume:
|
||||
|
||||
```bash
|
||||
# Windows / PowerShell
|
||||
pwsh ./scripts/dev-token.ps1
|
||||
pwsh ./scripts/dev-server-init.ps1
|
||||
|
||||
# Linux / macOS / any POSIX shell (including most AI agent sandboxes)
|
||||
bash ./scripts/dev-token.sh
|
||||
bash ./scripts/dev-server-init.sh
|
||||
```
|
||||
|
||||
Both scripts log in as the dev container's admin account, run the full
|
||||
OAuth PKCE flow against it, and write the resulting token to
|
||||
`.env.development.local` (gitignored, never committed). Tokens expire
|
||||
after 1 hour — re-run the script and restart `npm run dev` if the UI
|
||||
starts returning 401s.
|
||||
This completes the bootstrap wizard (default domain `example.org`, no TLS
|
||||
certificate request — safe for local/offline use), creates a real
|
||||
`devadmin@example.org` admin account, and sets the server's default OAuth
|
||||
access token lifetime to 3 hours. It's idempotent — safe to re-run, it
|
||||
no-ops once the server is already bootstrapped. You only need to re-run it
|
||||
after `docker compose down -v` (which wipes the volumes).
|
||||
|
||||
## 3. Run the WebUI
|
||||
## 3. Get an access token
|
||||
|
||||
For local development it's simpler to skip interactive login and use a
|
||||
bearer token directly via `VITE_ACCESS_TOKEN` (see `.env.development`).
|
||||
|
||||
```bash
|
||||
# Windows / PowerShell
|
||||
pwsh ./scripts/dev-token.ps1 # 3 hour token (server default)
|
||||
pwsh ./scripts/dev-token.ps1 -DurationSeconds 1800 # custom duration (30 min)
|
||||
|
||||
# Linux / macOS / any POSIX shell (including most AI agent sandboxes)
|
||||
bash ./scripts/dev-token.sh # 3 hour token
|
||||
bash ./scripts/dev-token.sh 1800 # custom duration (30 min)
|
||||
```
|
||||
|
||||
Both scripts authenticate as the `devadmin` account created in step 2 and
|
||||
create a Stalwart API key with the requested expiry (default 3 hours,
|
||||
overridable per invocation — this is a genuine per-request duration, not
|
||||
a global setting), then write its secret to `.env.development.local`
|
||||
(gitignored, never committed) as `VITE_ACCESS_TOKEN`. Re-run the script
|
||||
and restart `npm run dev` once the token expires (the UI starts returning
|
||||
401s).
|
||||
|
||||
The `STALWART_RECOVERY_ADMIN` account is intentionally not used here: it's
|
||||
a break-glass credential and its tokens always expire in a fixed 1 hour
|
||||
regardless of server configuration, so it can't honor a custom duration.
|
||||
|
||||
## 4. Run the WebUI
|
||||
|
||||
```bash
|
||||
npm install # first time only
|
||||
@@ -69,7 +97,7 @@ npm run dev
|
||||
Open `http://localhost:5173`. You should land directly in the admin panel
|
||||
(no login screen) since `VITE_ACCESS_TOKEN` is set.
|
||||
|
||||
## 4. Verify your change
|
||||
## 5. Verify your change
|
||||
|
||||
```bash
|
||||
npm run typecheck
|
||||
@@ -91,15 +119,17 @@ behavior):
|
||||
npm run dev:server:down
|
||||
docker compose down -v # also removes the stalwart-etc/stalwart-data volumes
|
||||
npm run dev:server
|
||||
bash ./scripts/dev-server-init.sh # re-run: fresh volume needs bootstrapping again
|
||||
```
|
||||
|
||||
## Notes for AI agents
|
||||
|
||||
- This whole workflow (steps 1–3) is scriptable end-to-end without a
|
||||
browser: `npm run dev:server`, then `bash scripts/dev-token.sh`, then
|
||||
the app is reachable at `http://localhost:5173` with
|
||||
`VITE_ACCESS_TOKEN` already set. Verify backend connectivity directly
|
||||
with `curl`, e.g. `curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/jmap/session`.
|
||||
- This whole workflow (steps 1–4) is scriptable end-to-end without a
|
||||
browser: `npm run dev:server`, then `bash scripts/dev-server-init.sh`
|
||||
(first time only), then `bash scripts/dev-token.sh`, then the app is
|
||||
reachable at `http://localhost:5173` with `VITE_ACCESS_TOKEN` already
|
||||
set. Verify backend connectivity directly with `curl`, e.g.
|
||||
`curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/jmap/session`.
|
||||
- Read [AGENTS.md](AGENTS.md) before touching anything under `src/` —
|
||||
the schema-fidelity rule applies to all development, local test server
|
||||
or not.
|
||||
|
||||
Reference in New Issue
Block a user