# 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

