Auth

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
}
TokenDefault TTLStoragePurpose
Access15 minutesClient onlyAuthenticate API requests via Authorization: Bearer
Refresh7 daysHashed in refresh_tokens tableObtain 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 fingerprint

Security features

FeatureDescription
Dual-key rotationSet JWT_SECRET_PREV to your old key when rotating. Tokens signed with the previous key remain valid until they expire — zero downtime.
Device fingerprintingWhen 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 hashingRefresh tokens are stored as SHA-256 hex in the database — a database leak does not expose usable tokens.
Type isolationRefresh tokens include a type: "refresh" claim. Access tokens cannot be used as refresh tokens and vice versa.
Token cleanupExpired 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