Loading...
Loading...
Authentication and authorization patterns - JWT, OAuth2, sessions, RBAC, ABAC, passkeys, MFA, identity-aware proxies, and Better Auth. Use for: authentication, jwt, oauth2, session, login, rbac, abac, passkey, mfa, totp, api key, token, cookie, csrf, bearer token, refresh token, oidc, cloudflare access, zero trust, Cf-Access-Jwt-Assertion, AUD tag, service auth, better auth.
npx skill4agent add 0xdarkmatter/claude-mods auth-opsWhat 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
│
└─ Internal tool / staff app with an existing IdP?
└─ Identity-aware proxy (Cloudflare Access)
├─ Authn enforced at the edge, before your origin
├─ Origin verifies the proxy's signed JWT (never a bare header)
└─ Best for: admin panels, partner portals, not consumer signupHeader.Payload.Signature
Header: { "alg": "RS256", "typ": "JWT" }
Payload: { "iss": "auth.example.com", "sub": "user_123", ... }
Signature: RSASHA256(base64(header) + "." + base64(payload), privateKey)| Claim | Name | Purpose | Example |
|---|---|---|---|
| Issuer | Who issued the token | |
| Subject | Who the token represents | |
| Expiration | When the token expires | |
| Issued At | When the token was created | |
| Audience | Intended recipient(s) | |
| JWT ID | Unique token identifier | |
| Not Before | Token not valid before this time | |
| 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 |
┌──────────┐ ┌──────────┐
│ 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)│
└──────────┘ └──────────┘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 chainHow 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| Setting | Value | Purpose |
|---|---|---|
| | Cookie sent only for same-site requests (best CSRF protection) |
| | Cookie sent for top-level navigations (good default) |
| | Cookie sent for cross-site requests (requires |
| | Cookie only sent over HTTPS |
| | Cookie not accessible via JavaScript (prevents XSS theft) |
| N/A | Requires Secure, no Domain, Path=/ (strictest) |
| N/A | Requires Secure flag |
| seconds | Cookie lifetime (prefer over |
| | Scope cookie to path (usually |
Set-Cookie: __Host-session=abc123;
Secure;
HttpOnly;
SameSite=Lax;
Max-Age=86400;
Path=/| 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 |
| 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 |
| 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) |
| Attempt | Response |
|---|---|
| 1-5 | Normal login |
| 6-10 | CAPTCHA required |
| 11-20 | Progressive delays (2s, 4s, 8s...) |
| 20+ | Temporary account lockout (15-30 min) |
| 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 |
workers_dev = falseProxy edge (authn) ──JWT header──> Origin verifies JWT ──> app user lookup ──> role/scope binding
│ │ 403 on any failure │ 403 if no row (server-side)
└ IdP / OTP login, sessions └ cached JWKS, └ proxy admits ≠ app authorizes
rate limits, bot defense refetch on unknown kidreferences/cloudflare-access.md| 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 ( |
| No token revocation strategy | Cannot invalidate compromised JWTs before expiry | Short expiry + refresh tokens, or maintain a blocklist |
CORS with | | Specify exact origin, set |
| Browser silently rejects the cookie | Always pair |
| 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 | Token meant for Service A accepted by Service B | Always validate |
| 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) |
| Trusting an identity-aware proxy's plain email header | Headers are attacker-settable on any unproxied path | Verify the proxy's signed JWT (sig + issuer + audience); close every path around the proxy |
| Auth-library middleware as the only session check | Framework middleware can be bypassed (Next.js CVE-2025-29927 class) | Re-check the session in the data-access layer / route handlers |
| File | Contents | Lines |
|---|---|---|
| JWT structure, signing, sessions, cookies, CSRF, storage | ~650 |
| OAuth2 flows, OIDC, provider integration, social login | ~700 |
| RBAC, ABAC, ReBAC, RLS, multi-tenant, audit logging | ~600 |
| Password hashing, MFA, rate limiting, API keys, reset flows | ~550 |
| Identity-aware proxies via Cloudflare Access: app/policy anatomy, token claims, JWT verification, closed-origin precondition, service auth, sessions/logout/SPA, local dev | ~330 |
| Better Auth library: server/client setup, adapters, session model, social login, plugin catalog (passkey/2FA/org/SSO), Hono integration, migration | ~240 |