---
name: auth-ops
description: "Authentication and authorization patterns - JWT, OAuth2, sessions, RBAC, ABAC, passkeys, and MFA. Use for: authentication, authorization, jwt, oauth, oauth2, session, login, rbac, abac, passkey, mfa, totp, api key, token, auth, cookie, csrf, cors credentials, bearer token, refresh token, oidc."
license: MIT
allowed-tools: "Read Write Bash"
metadata:
  author: claude-mods
  related-skills: security-ops, api-design-ops, postgres-ops
---

# Auth Operations

Comprehensive authentication and authorization patterns for secure application development across languages and frameworks.

## Authentication Method Decision Tree

Use this tree to select the right authentication strategy for your use case.

```
What are you building?
│
├─ Traditional web application (server-rendered)?
│  └─ Session-based authentication
│     ├─ Server stores session data (Redis/DB)
│     ├─ Session ID in httpOnly cookie
│     └─ Best for: monoliths, SSR apps, admin panels
│
├─ API consumed by multiple clients?
│  └─ JWT (JSON Web Tokens)
│     ├─ Stateless, self-contained tokens
│     ├─ Access token (short-lived) + refresh token (long-lived)
│     └─ Best for: microservices, mobile apps, SPAs via BFF
│
├─ Service-to-service communication?
│  └─ API keys or Client Credentials (OAuth2)
│     ├─ API keys: simple, scoped, rotatable
│     ├─ Client Credentials: OAuth2 standard, token-based
│     └─ Best for: internal services, third-party integrations
│
├─ Third-party login (Google, GitHub, etc.)?
│  └─ OAuth2 / OpenID Connect
│     ├─ Authorization Code + PKCE for web/mobile
│     ├─ Delegate identity to trusted providers
│     └─ Best for: consumer apps, social login
│
└─ Passwordless authentication?
   └─ Passkeys (WebAuthn) or Magic Links
      ├─ Passkeys: phishing-resistant, biometric/hardware
      ├─ Magic links: email-based, time-limited
      └─ Best for: high-security, modern UX
```

## JWT Quick Reference

### Structure

```
Header.Payload.Signature

Header:  { "alg": "RS256", "typ": "JWT" }
Payload: { "iss": "auth.example.com", "sub": "user_123", ... }
Signature: RSASHA256(base64(header) + "." + base64(payload), privateKey)
```

### Common Claims

| Claim | Name | Purpose | Example |
|-------|------|---------|---------|
| `iss` | Issuer | Who issued the token | `"auth.example.com"` |
| `sub` | Subject | Who the token represents | `"user_123"` |
| `exp` | Expiration | When the token expires | `1700000000` (Unix timestamp) |
| `iat` | Issued At | When the token was created | `1699999100` |
| `aud` | Audience | Intended recipient(s) | `"api.example.com"` |
| `jti` | JWT ID | Unique token identifier | `"a1b2c3d4"` (for revocation) |
| `nbf` | Not Before | Token not valid before this time | `1699999100` |

### Signing Algorithms

| Algorithm | Type | Key | Use When |
|-----------|------|-----|----------|
| **RS256** | Asymmetric (RSA) | Public/private key pair | Distributed systems, multiple verifiers |
| **ES256** | Asymmetric (ECDSA) | Public/private key pair | Same as RS256, smaller keys/signatures |
| **HS256** | Symmetric (HMAC) | Shared secret | Single service, simple setups |

**Rule of thumb:** Use asymmetric (RS256/ES256) when the token issuer and verifier are different services. Use HS256 only when a single service both creates and verifies tokens.

### Access + Refresh Token Pattern

```
┌──────────┐                    ┌──────────┐
│  Client   │─── login ────────>│  Auth    │
│           │<── access (15m) ──│  Server  │
│           │<── refresh (7d) ──│          │
│           │                   └──────────┘
│           │─── API call ─────>┌──────────┐
│           │    (access token) │ Resource │
│           │<── response ──────│  Server  │
│           │                   └──────────┘
│           │─── access expired │          │
│           │─── refresh ──────>│  Auth    │
│           │<── new access ────│  Server  │
│           │<── new refresh ───│  (rotate)│
└──────────┘                    └──────────┘
```

- **Access token:** Short-lived (5-15 minutes), used for API calls
- **Refresh token:** Long-lived (7-30 days), used to get new access tokens
- **Rotation:** Issue a new refresh token with each use, invalidate the old one
- **Family detection:** Track refresh token lineage; if a revoked token is reused, invalidate the entire family

## OAuth2 Flow Decision Tree

```
What type of client?
│
├─ Web app with backend (Next.js, Rails, Django)?
│  └─ Authorization Code + PKCE
│     ├─ Redirect user to authorization server
│     ├─ Receive code at callback URL
│     ├─ Exchange code for tokens server-side
│     └─ PKCE prevents code interception attacks
│
├─ SPA (React, Vue) without backend?
│  └─ Authorization Code + PKCE (via BFF)
│     ├─ Use a Backend-for-Frontend to handle tokens
│     ├─ Never store tokens in browser-accessible storage
│     └─ BFF proxies API calls with token attached
│
├─ Mobile app (iOS, Android)?
│  └─ Authorization Code + PKCE
│     ├─ Use custom URI scheme or universal links for redirect
│     ├─ PKCE is mandatory (public client)
│     └─ Store tokens in secure enclave/keystore
│
├─ Server-to-server (no user)?
│  └─ Client Credentials
│     ├─ Authenticate with client_id + client_secret
│     ├─ No user context, service-level access
│     └─ Token cached until expiry
│
├─ CLI tool or smart TV?
│  └─ Device Code
│     ├─ Display code and URL to user
│     ├─ User authenticates on another device
│     ├─ CLI/TV polls for completion
│     └─ Good UX for input-constrained devices
│
└─ Microservice acting on behalf of a user?
   └─ Token Exchange (RFC 8693)
      ├─ Exchange user's token for a scoped downstream token
      ├─ Maintains user context across services
      └─ Use `act` claim for delegation chain
```

## Authorization Model Decision Tree

```
How complex are your access control needs?
│
├─ Simple: just "can user X do action Y"?
│  └─ Permission-based (direct)
│     ├─ user_permissions table
│     ├─ Simple to implement, hard to scale
│     └─ Good for: small apps, prototypes
│
├─ Users grouped into roles with fixed permissions?
│  └─ RBAC (Role-Based Access Control)
│     ├─ Roles: admin, editor, viewer
│     ├─ Each role has a set of permissions
│     ├─ Users assigned one or more roles
│     └─ Good for: most apps, admin panels, team tools
│
├─ Decisions depend on attributes (time, location, resource owner)?
│  └─ ABAC (Attribute-Based Access Control)
│     ├─ Policies evaluate subject + resource + environment attributes
│     ├─ "Allow if user.department == resource.department AND time < 17:00"
│     ├─ Flexible but complex
│     └─ Good for: enterprise, compliance-heavy, context-dependent access
│
└─ Access based on relationships (owner, parent, shared with)?
   └─ ReBAC (Relationship-Based Access Control)
      ├─ Google Zanzibar model
      ├─ Tuples: user:alice#viewer@document:report
      ├─ Supports inheritance: folder viewer → document viewer
      ├─ Tools: OpenFGA, SpiceDB, Ory Keto
      └─ Good for: file sharing, nested resources, social features
```

## Session Management Quick Reference

### Cookie Security Settings

| Setting | Value | Purpose |
|---------|-------|---------|
| `SameSite` | `Strict` | Cookie sent only for same-site requests (best CSRF protection) |
| `SameSite` | `Lax` | Cookie sent for top-level navigations (good default) |
| `SameSite` | `None` | Cookie sent for cross-site requests (requires `Secure`) |
| `Secure` | `true` | Cookie only sent over HTTPS |
| `HttpOnly` | `true` | Cookie not accessible via JavaScript (prevents XSS theft) |
| `__Host-` prefix | N/A | Requires Secure, no Domain, Path=/ (strictest) |
| `__Secure-` prefix | N/A | Requires Secure flag |
| `Max-Age` | seconds | Cookie lifetime (prefer over `Expires`) |
| `Path` | `/` | Scope cookie to path (usually `/`) |

### Recommended Cookie Configuration

```
Set-Cookie: __Host-session=abc123;
  Secure;
  HttpOnly;
  SameSite=Lax;
  Max-Age=86400;
  Path=/
```

### Session Expiry Strategies

| Strategy | Typical Value | Notes |
|----------|---------------|-------|
| **Idle timeout** | 15-30 minutes | Reset on each request |
| **Absolute timeout** | 8-24 hours | Force re-authentication |
| **Sliding window** | 30 min idle, 8h max | Best balance |
| **Remember me** | 30 days | Extended session, reduced privileges |

## Password Handling Quick Reference

### Hashing Algorithms

| Algorithm | Verdict | Notes |
|-----------|---------|-------|
| **argon2id** | BEST | Memory-hard, resists GPU attacks, recommended by OWASP |
| **bcrypt** | GOOD | Battle-tested, cost factor 12+, 72-byte input limit |
| **scrypt** | GOOD | Memory-hard, less common library support |
| **PBKDF2** | ACCEPTABLE | FIPS compliant, use 600k+ iterations with SHA-256 |
| **SHA-256/512** | BAD | Too fast, no salt built-in, easily brute-forced |
| **MD5** | NEVER | Broken, rainbow tables widely available |

### Password Rules (NIST 800-63B)

| Rule | Guidance |
|------|----------|
| Minimum length | 8 characters (12+ recommended) |
| Maximum length | At least 64 characters |
| Complexity rules | Do NOT require special chars/uppercase/numbers |
| Breached password check | Check against known breached passwords (HaveIBeenPwned API) |
| Password hints | Do NOT allow |
| Forced rotation | Do NOT force periodic changes (only on breach) |
| Paste into password field | ALLOW (supports password managers) |

### Rate Limiting Login Attempts

| Attempt | Response |
|---------|----------|
| 1-5 | Normal login |
| 6-10 | CAPTCHA required |
| 11-20 | Progressive delays (2s, 4s, 8s...) |
| 20+ | Temporary account lockout (15-30 min) |

**Important:** Use consistent response times for both success and failure to prevent timing-based username enumeration.

## MFA Quick Reference

### Methods Ranked by Security

| Method | Security | UX | Notes |
|--------|----------|----|-------|
| **WebAuthn/Passkeys** | Highest | Good | Phishing-resistant, hardware-backed |
| **TOTP (Authenticator)** | High | Medium | App-based (Google/Microsoft Authenticator) |
| **Push notifications** | High | Good | Requires mobile app |
| **Email OTP** | Medium | Medium | Depends on email security |
| **SMS OTP** | Low | Easy | SIM swap vulnerable, use as fallback only |

### TOTP Implementation Checklist

- [ ] Generate 160-bit secret (base32 encoded)
- [ ] Build otpauth:// URI with issuer and account
- [ ] Display QR code for authenticator scanning
- [ ] Require verification of first code before enabling
- [ ] Accept current window +/- 1 (30-second steps)
- [ ] Generate 8-10 single-use backup codes
- [ ] Hash backup codes before storing
- [ ] Allow recovery via verified identity

### Passkey/WebAuthn Checklist

- [ ] Generate cryptographic challenge on server
- [ ] Set relying party ID (your domain)
- [ ] Store credential public key and ID
- [ ] Verify signature on authentication
- [ ] Support multiple credentials per user
- [ ] Handle platform vs cross-platform authenticators
- [ ] Provide fallback auth method

## Common Gotchas

| Gotcha | Why It's Dangerous | Fix |
|--------|--------------------|-----|
| JWT stored in localStorage | XSS can steal tokens, no expiry enforcement by browser | Use httpOnly cookies or BFF pattern |
| Missing PKCE in OAuth2 | Authorization code interception attacks possible | Always use PKCE, even for confidential clients |
| Role explosion in RBAC | Hundreds of roles become unmanageable | Move to ABAC or ReBAC for complex scenarios |
| String comparison for tokens | Timing attacks reveal token value character by character | Use constant-time comparison (`crypto.timingSafeEqual`) |
| No token revocation strategy | Cannot invalidate compromised JWTs before expiry | Short expiry + refresh tokens, or maintain a blocklist |
| CORS with `credentials: true` | `Access-Control-Allow-Origin: *` does not work with credentials | Specify exact origin, set `Access-Control-Allow-Credentials: true` |
| `SameSite=None` without `Secure` | Browser silently rejects the cookie | Always pair `SameSite=None` with `Secure` flag |
| Refresh token reuse without detection | Stolen refresh tokens grant indefinite access | Rotate refresh tokens, detect reuse (token families) |
| Using OAuth2 Implicit grant | Tokens exposed in URL fragment, no refresh tokens | Use Authorization Code + PKCE instead (Implicit is deprecated) |
| Password in URL or logs | URLs are logged by proxies, browsers, and servers | Always send credentials in request body or headers |
| Missing CSRF protection with cookies | Cookie-based auth is vulnerable to cross-site request forgery | Use SameSite cookies + CSRF tokens for state-changing ops |
| Long-lived access tokens (hours/days) | Large attack window if token is compromised | Keep access tokens to 5-15 minutes, use refresh tokens |
| Storing API keys in plaintext | Database breach exposes all keys | Hash stored keys (SHA-256 of key), store prefix for lookup |
| Not validating JWT `aud` claim | Token meant for Service A accepted by Service B | Always validate `aud` matches your service identifier |
| Session fixation | Attacker sets session ID before login, then hijacks it | Regenerate session ID after authentication |
| Hardcoded secrets in code | Secrets leak via source control | Use environment variables or secret managers (Vault, AWS SSM) |

## Reference Files

| File | Contents | Lines |
|------|----------|-------|
| `references/jwt-sessions.md` | JWT structure, signing, sessions, cookies, CSRF, storage | ~650 |
| `references/oauth2-oidc.md` | OAuth2 flows, OIDC, provider integration, social login | ~700 |
| `references/authorization.md` | RBAC, ABAC, ReBAC, RLS, multi-tenant, audit logging | ~600 |
| `references/implementation.md` | Password hashing, MFA, rate limiting, API keys, reset flows | ~550 |

## See Also

- **security-ops** - Broader security patterns: OWASP, headers, input validation, encryption
- **api-design-ops** - API design including authentication endpoints, rate limiting
- **postgres-ops** - Row-level security (RLS) policies for database authorization
