Authentication
Open Astra uses JWT-based authentication with a dual-token pattern: short-lived access tokens for API requests and long-lived refresh tokens for session continuity. Passwords are hashed with bcrypt (12 rounds), refresh tokens are stored hashed (SHA-256) in PostgreSQL, and optional device fingerprinting binds tokens to specific client/IP combinations.
Authentication flow
text
┌──────────┐ POST /auth/register ┌─────────┐
│ Client │ ──────────────────────→ │ Gateway │ → hash password (bcrypt, 12 rounds)
│ │ { email, password } │ │ → store user in users table
│ │ │ │ → return { accessToken, refreshToken }
│ │ │ │
│ │ POST /auth/login │ │
│ │ ──────────────────────→ │ │ → verify password
│ │ │ │ → sign access token (15m) + refresh (7d)
│ │ │ │ → store hashed refresh token
│ │ │ │
│ │ POST /auth/refresh │ │
│ │ ──────────────────────→ │ │ → verify refresh token
│ │ { refreshToken } │ │ → revoke old, issue new pair
│ │ │ │
│ │ POST /auth/logout │ │
│ │ ──────────────────────→ │ │ → revoke refresh token
└──────────┘ └─────────┘Token pair
Every successful login or registration returns a pair of HS256-signed JWTs. The access token is stateless and verified on every request. The refresh token is stored hashed in the database and can be revoked at any time.
json
// Access token — short-lived, stateless
{
"uid": "user-abc",
"sub": "user-abc",
"iat": 1709251200,
"exp": 1709252100, // +15 minutes
"dfp": "a1b2c3d4e5f6" // optional device fingerprint
}
// Refresh token — long-lived, stored hashed in DB
{
"uid": "user-abc",
"type": "refresh", // prevents cross-use
"sub": "user-abc",
"iat": 1709251200,
"exp": 1709856000 // +7 days
}| Token | Default TTL | Storage | Purpose |
|---|---|---|---|
| Access | 15 minutes | Client only | Authenticate API requests via Authorization: Bearer |
| Refresh | 7 days | Hashed in refresh_tokens table | Obtain new access/refresh pair without re-entering credentials |
Configuration
bash
# Required
JWT_SECRET=your-signing-key-min-32-chars
# Optional
JWT_SECRET_PREV=old-key-for-rotation # zero-downtime key rotation
JWT_ACCESS_EXPIRES=15m # access token TTL (default: 15m)
JWT_REFRESH_EXPIRES=7d # refresh token TTL (default: 7d)
BIND_JWT_TO_DEVICE=true # bind tokens to device fingerprintSecurity features
| Feature | Description |
|---|---|
| Dual-key rotation | Set JWT_SECRET_PREV to your old key when rotating. Tokens signed with the previous key remain valid until they expire — zero downtime. |
| Device fingerprinting | When BIND_JWT_TO_DEVICE=true, a dfp claim is embedded in access tokens — SHA-256 of userAgent + IP subnet. Tokens are rejected if presented from a different device/network. |
| Refresh token hashing | Refresh tokens are stored as SHA-256 hex in the database — a database leak does not expose usable tokens. |
| Type isolation | Refresh tokens include a type: "refresh" claim. Access tokens cannot be used as refresh tokens and vice versa. |
| Token cleanup | Expired and revoked refresh tokens are deleted daily at 3 AM by the Scheduler. |
Explore
- JWT & Tokens — token signing, verification, rotation, and fingerprinting details
- Sessions — session lifecycle, compaction, and conversation state
- Auth Hardening — CSRF, rate limiting, and additional security measures