ADR 0005: JWT for Authentication
Status: Accepted
Date: 2025-02-10
Track: Software
Context
KoproGo needs stateless authentication supporting:
Multi-role users (Owner, Syndic, BoardMember, Accountant)
Role switching without re-login
API access for mobile apps (future)
Refresh tokens for long sessions
Decision
We use JSON Web Tokens (JWT) with:
Access token: Short-lived (1h), contains user ID + active role
Refresh token: Long-lived (30d), stored in database for revocation
HS256 algorithm: Symmetric signing (sufficient for single-server MVP)
Bearer authentication:
Authorization: Bearer <token>header
Token structure:
{
"sub": "user-uuid",
"email": "syndic@example.com",
"active_role_id": "role-uuid",
"exp": 1234567890
}
Consequences
Positive:
✅ Stateless: No server-side session storage (scales horizontally)
✅ Multi-role support: Token carries active role, switch via
/auth/switch-role✅ API-friendly: Standard Bearer auth for mobile/third-party clients
✅ Revocable: Refresh tokens stored in DB, can be revoked
Negative:
⚠️ Cannot revoke access tokens: Must wait for expiration (1h max)
⚠️ Token size: Larger than session IDs (~200 bytes vs 16 bytes)
Security measures:
bcrypt password hashing (cost 12)
HTTPS only in production
Refresh token rotation on use
expclaim prevents replay attacks
Alternatives Considered
Session cookies:
✅ Smaller, revocable immediately
❌ Requires server-side storage (Redis, PostgreSQL)
❌ Less API-friendly for mobile
Verdict: Rejected for statelessness
OAuth2 (third-party):
✅ Delegate auth to Google/Microsoft
❌ Vendor lock-in, privacy concerns
Verdict: Future consideration, not MVP
Implementation
Library: jsonwebtoken crate
Secret: JWT_SECRET in .env (64+ random chars)
Endpoints:
POST /auth/login- Returns access + refresh tokensPOST /auth/refresh- Exchange refresh token for new access tokenPOST /auth/switch-role- Change active role (issues new access token)POST /auth/logout- Revoke refresh token
Next Steps
✅ Implement JWT middleware (Done)
⏳ Switch to RS256 (asymmetric) for multi-server Phase 2
⏳ Add rate limiting on
/auth/loginto prevent brute force⏳ Consider adding 2FA for board members (high-value approvals)
References
JWT spec: https://datatracker.ietf.org/doc/html/rfc7519
jsonwebtoken crate: https://github.com/Keats/jsonwebtoken