Auth¶
The auth service handles all authentication for the Jarvis platform: user JWT tokens (login, register, refresh, logout), password reset, app-to-app authentication between services, and node authentication for Pi Zero devices.
Quick Reference¶
| Port | 7701 |
| Health endpoint | GET /health |
| Source | jarvis-auth/ |
| Framework | FastAPI + Uvicorn |
| Database | PostgreSQL |
| Tier | 1 - Core Infrastructure |
API Endpoints¶
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
POST |
/api/v0/auth/register |
Register a new user |
POST |
/api/v0/auth/login |
Login, returns JWT access + refresh tokens. Response includes must_change_password when the account has a pending admin-issued temp password |
POST |
/api/v0/auth/refresh |
Returns new access token + a newly rotated refresh token — client must persist the new refresh token or the next refresh will fail. Rejects inactive users and expired temp passwords (401) |
POST |
/api/v0/auth/change-password |
Verifies the current password, sets a new one, revokes all other refresh tokens, and returns a fresh token pair the client must adopt. Clears any pending must_change_password flag |
POST |
/api/v0/auth/logout |
Authenticates with the refresh token itself (not a Bearer header), so it works even after the access token has expired. Revokes that token's rotation family, or every session for the user with all_devices: true. Always returns 204, even for an unknown/already-revoked token |
GET |
/api/v0/superuser/users |
Superuser only. Lists all users with household memberships — backs the admin Users panel |
POST |
/api/v0/superuser/users/{id}/temp-password |
Superuser only. Issues a show-once temp password for a user (bcrypt-stored, expires after TEMP_PASSWORD_EXPIRE_HOURS). Sets must_change_password and revokes every existing session for that user. Optional body: {temp_password?, expires_in_hours?} |
POST |
/internal/validate-app |
Validate app-to-app credentials |
POST |
/internal/validate-node |
Validate node credentials |
GET |
/api/v0/users/{id} |
Get user info by ID |
Password Reset (No Email in the Stack)¶
There is no email service, so password recovery is superuser-driven instead of a reset-link flow:
- A superuser calls
POST /superuser/users/{id}/temp-password(via the admin Users panel or directly) to issue a temp password for a locked-out user. This setsmust_change_password = trueon the account and revokes every existing session. - The user logs in with the temp password. The login response's
must_change_password: truefield signals clients (mobile, web) to force the change-password flow before letting the user proceed. - The user calls
POST /auth/change-passwordwith the temp password ascurrent_passwordand a new password. This clearsmust_change_password, revokes all other sessions, and returns a fresh token pair.
If the temp password expires before use (TEMP_PASSWORD_EXPIRE_HOURS, default 24h), login returns a distinct 401 telling the user to ask an administrator for a new one.
change-password also works as a general self-service password change outside the temp-password flow — it always requires the current password.
Logout and Session Revocation¶
POST /auth/logout authenticates with the refresh token in the request body rather than a Bearer access token, so logout still works after the access token has expired:
- Default: revokes only the presented token's rotation family (that one device/session).
all_devices: true: revokes every refresh token for the user (all sessions).- Always returns
204regardless of whether the token was found, valid, or already revoked — the response never reveals token state.
Admin-issued temp passwords and self-service change-password both revoke all of a user's sessions, not just the current one.
Token Rotation¶
Refresh tokens are rotated on every /auth/refresh call (since 2026-05). Each refresh mints a new refresh token chained to the previous one by a family_id. Clients must persist and use the newly returned refresh token — replaying an already-rotated token returns 401.
A 10-second in-process grace window (REFRESH_TOKEN_GRACE_SECONDS) lets a benign double-submit (two concurrent refresh callers, a lost response) re-get the cached successor without failing. This grace cache is in-process only — auth must stay single-worker until backed by Redis/Postgres.
Stale replay behavior (default): a replay of an already-rotated token is rejected (401) but does not revoke the entire token family. The live tail of the chain keeps working, so the session survives auth restarts and benign double-submits over flaky connections. Enable strict whole-family revocation with REFRESH_TOKEN_REVOKE_FAMILY_ON_REUSE=true if you need theft detection and can accept periodic spurious logouts from mobile clients on unreliable links.
Environment Variables¶
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
MIGRATIONS_DATABASE_URL |
PostgreSQL connection string for Alembic migrations (use localhost even when running in Docker) |
AUTH_SECRET_KEY |
JWT signing key — generate with openssl rand -hex 32 |
AUTH_ALGORITHM |
JWT signing algorithm (default HS256) |
ACCESS_TOKEN_EXPIRE_MINUTES |
JWT access token TTL in minutes |
REFRESH_TOKEN_EXPIRE_DAYS |
Refresh token TTL in days (default 14). Keep >=7 so mobile users are not logged out weekly. |
REFRESH_TOKEN_GRACE_SECONDS |
Grace window for benign double-submits of a just-rotated token (default 10). |
REFRESH_TOKEN_REVOKE_FAMILY_ON_REUSE |
When a stale rotated token is replayed, revoke the entire token family (default false). Off is recommended for most setups — a mobile client on a flaky link replays far more often than tokens are stolen. Enable only if you need stricter theft response and can tolerate the resulting spurious logouts. |
TEMP_PASSWORD_EXPIRE_HOURS |
Expiry window for a superuser-issued temp password (default 24). Login rejects an expired temp password with a distinct 401 telling the user to request a new one. |
JARVIS_AUTH_ADMIN_TOKEN |
Token for admin endpoints — generate with openssl rand -hex 32 |
JARVIS_APP_ID |
App identity for service-to-service auth with jarvis-logs (default jarvis-auth) |
JARVIS_APP_KEY |
App key for service-to-service auth with jarvis-logs |
JARVIS_LOG_CONSOLE_LEVEL |
Console log level (default INFO) |
JARVIS_LOG_REMOTE_LEVEL |
Remote log level sent to jarvis-logs (default DEBUG) |
Dependencies¶
- PostgreSQL -- user accounts, app credentials, node credentials, refresh tokens
- jarvis-logs -- structured logging (optional, degrades to console)
Dependents¶
Nearly every service depends on auth for request validation:
- jarvis-command-center, jarvis-whisper-api, jarvis-tts, jarvis-ocr-service, jarvis-recipes-server, jarvis-logs, jarvis-notifications, jarvis-settings-server, jarvis-mcp, jarvis-admin, jarvis-config-service
The admin Users panel (jarvis-admin) and the mobile/web forced-password-change gates (jarvis-node-mobile, jarvis-web) consume the superuser and change-password endpoints directly.
Impact if Down¶
- No new user logins or token refreshes
- No password resets or changes; locked-out users cannot recover access
- App-to-app authentication validation fails (services cannot verify each other)
- Node authentication fails (Pi Zero devices cannot connect)
- Services with cached/valid JWTs may continue briefly