Authentication
Built-in JWT authentication with Argon2id hashing, refresh tokens, and server-side session management. No external auth provider needed.
Tokens
HS256-signed JWTs.
| Token | Lifetime | Purpose |
|---|---|---|
| Access token | 15 minutes | Sent as Authorization: Bearer <token> on every request. |
| Refresh token | 30 days | Used to get a new access token without re-entering credentials. |
Access token payload:
{
"sub": "01926b3e-...",
"username": "alice",
"exp": 1736937100,
"iat": 1736936200
}
Security Details
| Property | Value |
|---|---|
| Hashing | Argon2id with OWASP-recommended parameters |
| JWT signing | HS256 |
| JWT secret | ROOTCX_JWT_SECRET env var, or auto-generated in config/jwt.key |
| Session store | Server-side in rootcx_system.sessions. Allows immediate logout. |
| Password storage | Only the Argon2id hash is stored. Plaintext is never persisted. |
Using Studio
Manage users through the Auth panel in Studio, where you can view registered users and reset passwords.
Using Code
Registration
POST /api/v1/auth/register
{ "username": "alice", "password": "Str0ngPass!x" }
Creates a user in rootcx_system.users. The password is hashed with Argon2id (OWASP-recommended).
Password requirements: minimum 10 characters, at least one uppercase letter, one lowercase letter, and one digit.
admin role on the core app. This bootstraps initial access — subsequent users must be assigned roles explicitly.Login
POST /api/v1/auth/login
{ "username": "alice", "password": "Str0ngPass!x" }
Returns an access token, a refresh token, and the user object:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 900,
"user": {
"id": "01926b3e-...",
"username": "alice",
"email": "alice@example.com",
"displayName": "Alice Martin",
"createdAt": "2024-12-01T10:00:00+00:00"
}
}
Token Refresh
POST /api/v1/auth/refresh
{ "refreshToken": "..." }
Returns { "accessToken": "...", "expiresIn": 900 }. The refresh token itself is not rotated.
Logout
POST /api/v1/auth/logout
{ "refreshToken": "..." }
Invalidates the session server-side. The refresh token can no longer be used. Access tokens already issued remain valid until they expire (max 15 minutes).