Files
goplt/docs/content/adr/0017-jwt-token-strategy.md

1.9 KiB

ADR-0017: JWT Token Strategy

Status

Accepted

Context

The platform needs authentication tokens that:

  • Are stateless (no server-side session storage)
  • Support role and permission claims
  • Can be revoked (challenge)
  • Have appropriate lifetimes
  • Support multi-tenancy (tenant ID in claims)

Token strategies considered:

  1. Short-lived access tokens + long-lived refresh tokens - Industry standard
  2. Single long-lived tokens - Simple but insecure
  3. Short-lived tokens only - Secure but poor UX
  4. Session-based - Stateful, requires storage

Decision

Use short-lived access tokens + long-lived refresh tokens:

  1. Access tokens: 15 minutes lifetime, contain user ID, roles, tenant ID
  2. Refresh tokens: 7 days lifetime, stored in database (for revocation)
  3. Token format: JWT with claims: sub (user ID), roles, tenant_id, exp
  4. Revocation: Refresh tokens stored in DB, can be revoked/deleted

Rationale:

  • Industry best practice (OAuth2/OIDC pattern)
  • Good balance of security and UX
  • Access tokens can't be revoked (short lifetime mitigates risk)
  • Refresh tokens can be revoked (stored in DB)
  • Supports stateless authentication for most requests

Consequences

Positive

  • Secure (short access token lifetime)
  • Good UX (refresh tokens prevent frequent re-login)
  • Stateless for most requests (access tokens)
  • Supports revocation (refresh tokens)

Negative

  • Requires refresh token storage (DB table)
  • More complex than single token
  • Need to handle token refresh flow

Implementation Notes

  • Use github.com/golang-jwt/jwt/v5 for JWT handling
  • Store refresh tokens in refresh_tokens table (user_id, token_hash, expires_at)
  • Generate access tokens with HS256 or RS256 signing
  • Include roles in token claims (not just role IDs)
  • Validate token signature and expiration on each request
  • Refresh endpoint validates refresh token and issues new access token