Task: Implement JWT refresh
This page documents a task in the JWT refresh story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Close the loop on JWT auth with refresh + telemetry, so long-running sessions don't expire silently.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | JWT refresh |
| Now | Completed 2026-03-21. |
| Waiting on | None. |
| Next | None. |
| Last touched | 2026-03-21 |
Acceptance
- Configurable lifetimes via iam.token.access_lifetime_seconds + party_selection_lifetime_seconds + max_session_seconds + refresh_threshold_pct.
- iam.v1.auth.refresh NATS endpoint + validate_allow_expired() in JWT authenticator.
- ores_iam_auth_events_tbl TimescaleDB hypertable with hourly + daily continuous aggregates (90-day raw, 3-year daily retention).
- make_request_context() returns std::expected with explicit token_expired / unauthorized (no more silent fallback).
- error_reply() helper sends X-Error NATS headers.
- 48 domain handler files updated to handle the new std::expected return type.
- account_handler + auth_handler hot-reload token settings on system_setting_changed.
- Shell: nats_session::authenticated_request validates X-Error; on token_expired refreshes + retries once; max_session_exceeded throws.
- Qt: ClientManager QTimer fires at 80% of token lifetime; sessionExpired signal connects to MainWindow → warning dialog + re-login.
Plan
Captured during execution; cleared into the parent story on close.
Notes
Builds on the RS256 + JWT-in-claims work from sprint 14; the telemetry hypertable is the load-bearing addition.
Result
JWT refresh works end-to-end; auth telemetry persisted.