Files
claudekit/skills/authentication/references/auth-flows.md
T
2026-04-19 14:10:38 +07:00

238 lines
13 KiB
Markdown

# Authentication Flows Quick Reference
## Decision Tree: Which Auth Method?
```
What are you building?
├─ Server-rendered web app (Next.js, Django, Rails)?
│ └─> SESSION-BASED AUTH
│ - HttpOnly cookies, server-side session store
│ - Simple, secure, well-understood
├─ SPA + API backend (same domain)?
│ └─> SESSION-BASED AUTH (still preferred)
│ - Cookies sent automatically, no JS token handling
│ - Or: JWT in HttpOnly cookie (not localStorage)
├─ SPA + API backend (different domain)?
│ └─> JWT with access + refresh tokens
│ - Access token: short-lived, in memory
│ - Refresh token: HttpOnly cookie
├─ Mobile app?
│ └─> JWT with access + refresh tokens
│ - Store refresh token in secure storage (Keychain/Keystore)
│ - Access token in memory
├─ Third-party API access?
│ └─> OAuth2 + API keys
│ - OAuth2 for user-delegated access
│ - API keys for server-to-server
├─ Machine-to-machine (service-to-service)?
│ └─> API KEYS or OAuth2 Client Credentials
│ - API key: simple, rotate regularly
│ - Client Credentials: when you need scoped access
└─ CLI tool?
└─> OAuth2 Device Code Flow or API key
```
---
## JWT Access + Refresh Token Flow
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Auth │ │ API │
│ (SPA/ │ │ Server │ │ Server │
│ Mobile) │ │ │ │ │
└────┬──────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ 1. POST /auth/login │ │
│ { email, password } │ │
│─────────────────────────────>│ │
│ │ │
│ 2. 200 OK │ │
│ { access_token (15min) } │ │
│ Set-Cookie: refresh_token │ │
│ (HttpOnly, Secure, 7d) │ │
│<─────────────────────────────│ │
│ │ │
│ 3. GET /api/data │ │
│ Authorization: Bearer <access_token> │
│─────────────────────────────────────────────────────────────>│
│ │ │
│ 4. 200 OK { data } │ │
│<─────────────────────────────────────────────────────────────│
│ │ │
│ ── access_token expires ── │ │
│ │ │
│ 5. GET /api/data │ │
│ Authorization: Bearer <expired_token> │
│─────────────────────────────────────────────────────────────>│
│ │ │
│ 6. 401 { code: "token_expired" } │
│<─────────────────────────────────────────────────────────────│
│ │ │
│ 7. POST /auth/refresh │ │
│ Cookie: refresh_token │ │
│─────────────────────────────>│ │
│ │ │
│ 8. 200 { new access_token } │ │
│ Set-Cookie: new refresh │ │
│<─────────────────────────────│ │
│ │ │
│ 9. Retry original request with new access_token │
│─────────────────────────────────────────────────────────────>│
```
### JWT Best Practices
| Concern | Recommendation |
|---------|---------------|
| Access token lifetime | 5-15 minutes |
| Refresh token lifetime | 7-30 days |
| Access token storage | Memory only (JS variable) |
| Refresh token storage | HttpOnly Secure cookie (web) or Keychain (mobile) |
| Token rotation | Issue new refresh token on each refresh |
| Revocation | Maintain server-side deny list for refresh tokens |
| Algorithm | RS256 (asymmetric) for distributed systems, HS256 for single server |
| Claims | Minimal: sub, exp, iat, roles/permissions |
---
## OAuth2 Authorization Code + PKCE Flow
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Auth │ │ Resource │
│ (Browser)│ │ Provider│ │ Server │
└────┬──────┘ └────┬─────┘ └────┬──────┘
│ │ │
│ 1. Generate: │ │
│ code_verifier (random 43-128 chars) │
│ code_challenge = SHA256(code_verifier) │
│ │ │
│ 2. Redirect to: │ │
│ /authorize? │ │
│ response_type=code │
│ client_id=xxx │ │
│ redirect_uri=https://app/callback │
│ scope=openid profile │
│ state=random_csrf_token │
│ code_challenge=xxx │
│ code_challenge_method=S256 │
│────────────────────>│ │
│ │ │
│ 3. User logs in │ │
│ and consents │ │
│ │ │
│ 4. Redirect to: │ │
│ /callback?code=AUTH_CODE&state=xxx │
│<────────────────────│ │
│ │ │
│ 5. Verify state matches │
│ │ │
│ 6. POST /token │ │
│ { grant_type=authorization_code, │
│ code=AUTH_CODE, │ │
│ redirect_uri=...,│ │
│ code_verifier=ORIGINAL_VERIFIER } │
│────────────────────>│ │
│ │ │
│ 7. { access_token, │ │
│ refresh_token,│ │
│ id_token } │ │
│<────────────────────│ │
│ │ │
│ 8. GET /api/resource │
│ Authorization: Bearer <access_token> │
│───────────────────────────────────────────>│
```
### PKCE Key Points
| Term | Purpose |
|------|---------|
| `code_verifier` | Random string (43-128 chars), stored client-side |
| `code_challenge` | `BASE64URL(SHA256(code_verifier))` sent in auth request |
| `state` | CSRF protection (random, verify on callback) |
| PKCE purpose | Prevents auth code interception (no client secret needed) |
---
## Session-Based Auth Flow
```
┌──────────┐ ┌──────────────────┐
│ Browser │ │ Server │
│ │ │ (session store) │
└────┬──────┘ └────┬──────────────┘
│ │
│ 1. POST /login │
│ { email, password } │
│─────────────────────────────>│
│ │ 2. Verify credentials
│ │ 3. Create session in store
│ │ (Redis/DB/memory)
│ 4. Set-Cookie: │
│ session_id=abc123; │
│ HttpOnly; Secure; │
│ SameSite=Lax; Path=/ │
│<─────────────────────────────│
│ │
│ 5. GET /dashboard │
│ Cookie: session_id=abc123 │ (sent automatically)
│─────────────────────────────>│
│ │ 6. Lookup session abc123
│ │ 7. Attach user to request
│ 8. 200 OK │
│<─────────────────────────────│
│ │
│ 9. POST /logout │
│─────────────────────────────>│
│ │ 10. Delete session from store
│ 11. Clear cookie │
│<─────────────────────────────│
```
### Session Cookie Settings
| Attribute | Value | Purpose |
|-----------|-------|---------|
| `HttpOnly` | Always | Prevent JS access (XSS protection) |
| `Secure` | Always in prod | Only send over HTTPS |
| `SameSite` | `Lax` (default) | CSRF protection (allows top-level navigation) |
| `SameSite` | `Strict` | Stronger CSRF (breaks external link login) |
| `Path` | `/` | Cookie scope |
| `Max-Age` | 86400-2592000 | Session duration (1-30 days) |
| `Domain` | Omit or explicit | Cookie scope to domain |
---
## Comparison: JWT vs Sessions vs API Keys
| Aspect | JWT | Sessions | API Keys |
|--------|-----|----------|----------|
| Stateless | Yes (no server lookup) | No (server-side store) | No (server-side lookup) |
| Revocation | Hard (needs deny list) | Easy (delete session) | Easy (delete key) |
| Scaling | Easy (no shared state) | Needs shared session store | Needs shared key store |
| Security | Token theft = access until expiry | Session theft = access until revoked | Key theft = access until rotated |
| Best for | Distributed APIs, mobile | Web apps, SSR | Service-to-service, CLI |
| CSRF risk | Low (if not in cookie) | Needs CSRF tokens | N/A (header-based) |
| XSS risk | High if in localStorage | Low (HttpOnly cookie) | Low (server-side only) |
### API Key Best Practices
| Practice | Details |
|----------|---------|
| Prefix keys | `sk_live_`, `pk_test_` (identify type/env) |
| Hash before storing | Store `SHA256(key)`, never plaintext |
| Scope keys | Limit permissions per key |
| Set expiry | Auto-expire, require rotation |
| Rate limit per key | Prevent abuse |
| Transmit in header | `Authorization: Bearer <key>` or `X-API-Key: <key>` |
| Never in URL | Query params end up in logs and browser history |