JWT Tokens

tsdevstack uses JSON Web Tokens (JWT) for stateless authentication. Tokens are issued by the auth service and validated by Kong before requests reach your backend services.

Token types

Access tokens

Short-lived tokens used for API authentication:

  • Lifetime: Configurable via ACCESS_TOKEN_TTL
  • Usage: Sent in Authorization header
  • Algorithm: RS256 (RSA signatures)
// Access token payload
{
  sub: 'user-123',           // User ID
  email: 'user@example.com',
  systemRole: 'USER',        // System role: USER or ADMIN
  roles: [],                 // Custom roles (see Roles)
  confirmed: true,           // Email confirmation status
  status: 'ACTIVE',          // Account status
  iss: 'auth-service',       // Issuer
  aud: 'kong',               // Audience (Kong validates this)
  iat: 1234567890,           // Issued at
  exp: 1234568790            // Expiration
}

Refresh tokens

Longer-lived tokens used to obtain new access tokens:

  • Lifetime: Configurable via REFRESH_TOKEN_TTL
  • Format: Random 64-byte hex string (not a JWT)
  • Storage: SHA-256 hashed before storing in database
  • Usage: Exchanged for new access tokens via refresh endpoint
  • Rotation: Each refresh issues a new token and invalidates the old one
  • Fresh claims: A refresh reads the user from the database again, so changes to roles or confirmation status reach the access token at the next refresh (Roles)

Token validation flow

  1. Client sends request with Authorization: Bearer <token>
  2. Kong validates JWT signature against JWKS endpoint
  3. Kong extracts claims and passes user info to backend
  4. Backend AuthGuard verifies the Kong trust token, then makes user info available via req.user

Note: JWT validation only happens for routes with @ApiBearerAuth(). See Two-layer authentication for how Kong and AuthGuard work together.

JWKS endpoint

The auth service exposes a JWKS (JSON Web Key Set) endpoint for public key discovery:

GET /auth/.well-known/jwks.json

Note: Route prefixes use the short service name (e.g., /auth/) not the full package name.

This enables key rotation without service restarts - Kong fetches keys dynamically.

Configuration

JWT keys are managed through the secrets system. The framework generates RSA key pairs automatically.

SecretDescription
JWT_PRIVATE_KEY_CURRENTRSA private key for signing
JWT_PUBLIC_KEY_CURRENTRSA public key for verification
JWT_KEY_ID_CURRENTKey ID (kid) for JWKS
ACCESS_TOKEN_TTLAccess token lifetime in seconds
REFRESH_TOKEN_TTLRefresh token lifetime in seconds

To customize token expiry, add to .secrets.user.json:

{
  "secrets": {
    "ACCESS_TOKEN_TTL": "1800",
    "REFRESH_TOKEN_TTL": "1209600"
  }
}

OWASP alignment

The auth service template implements cryptographic best practices aligned with OWASP guidelines:

PracticeImplementationOWASP Reference
Asymmetric signingRS256 (RSA-SHA256) — preferred over symmetric HS256JWT Cheat Sheet
Key rotationCurrent + previous key pairs with kid headersKey Management Cheat Sheet
Short-lived access tokensConfigurable TTL (default 15 min)JWT Cheat Sheet
Refresh token hashingSHA-256 hash stored in database, never plain textPassword Storage Cheat Sheet
Refresh token rotationEach refresh issues a new token and invalidates the old oneSession Management Cheat Sheet
CSPRNG token generationcrypto.randomBytes (64 bytes for refresh tokens)Cryptographic Storage Cheat Sheet
Password hashingbcrypt with configurable rounds (default 12)Password Storage Cheat Sheet
Timing-safe comparisoncrypto.timingSafeEqual for token/key validationAuthentication Cheat Sheet

Key rotation

The framework supports seamless key rotation:

  1. Generate new keys and set as JWT_*_CURRENT
  2. Move old keys to JWT_*_PREVIOUS
  3. JWKS endpoint returns both keys
  4. Existing tokens remain valid until expiry