ADR-012: Use JWT for Authentication¶
Status: Accepted
Date: 2024-12-01 \nDeciders: Development Team
TL;DR¶
Use JSON Web Tokens (JWT) for stateless authentication across our SPA and future microservices. Supports OIDC integration with Microsoft Entra ID.
Context¶
Need authentication mechanism for distributed system that:
- Works with Vue.js SPA
- Supports multiple services (stateless)
- Integrates with MS Entra ID (OIDC)
- Minimizes server-side session storage
Options: JWT tokens vs session-based authentication.
Decision¶
Use JWT tokens for authentication.
Why JWT wins:
- Stateless (no server-side session storage)
- Microservices-friendly (no shared session store)
- Works seamlessly with SPAs and CORS
- Standard format (RFC 7519)
- OIDC-compatible (MS Entra ID ready)
- Mobile-ready for future apps
Alternatives Considered¶
Session-Based Authentication
✗ Requires Redis/DB for sessions, shared store complexity, CORS issues
✓ Easy revocation, smaller token size, server control ✗ Not part of the spec requirement
Consequences¶
Positive:
- Improved scalability (stateless)
- No server-side session storage
- Simplified microservices authentication
- Industry-standard approach
- Good tooling support (joserfc, authlib)
Negative:
- Larger token size vs session IDs
- Cannot revoke tokens before expiration (without blacklist)
- Must not store sensitive data in tokens
- Requires careful expiration strategy
Mitigation:
- Short-lived access tokens (15-60 min)
- Optional refresh tokens for session continuity
- Future|Optional: Redis blacklist for token revocation
- HTTPS-only transmission
- JWT in cookie httpOnly
- bcrypt password hashing
Implementation¶
# Token generation
from joserfc import jwt
def create_access_token(user: User) -> str:
payload = {
"sub": user.id,
"email": user.email,
"roles": user.roles,
"exp": datetime.utcnow() + timedelta(minutes=30)
}
return jwt.encode(header, payload, secret_key)
# Token validation
@router.get("/protected")
async def protected_route(user: User = Depends(get_current_user)):
# JWT validated by get_current_user dependency
return {"message": f"Hello {user.email}"}
Token Types:
- Access tokens (30 min) - API authorization
- Refresh tokens (optional) - Session continuity
- ID tokens (OIDC) - User identification