mirror of
https://github.com/duthaho/claudekit.git
synced 2026-06-10 12:14:57 +03:00
238 lines
13 KiB
Markdown
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 |
|