1.9 KiB
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:
- Short-lived access tokens + long-lived refresh tokens - Industry standard
- Single long-lived tokens - Simple but insecure
- Short-lived tokens only - Secure but poor UX
- Session-based - Stateful, requires storage
Decision
Use short-lived access tokens + long-lived refresh tokens:
- Access tokens: 15 minutes lifetime, contain user ID, roles, tenant ID
- Refresh tokens: 7 days lifetime, stored in database (for revocation)
- Token format: JWT with claims:
sub(user ID),roles,tenant_id,exp - 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/v5for JWT handling - Store refresh tokens in
refresh_tokenstable (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