Authentication and authorization patterns — JWT, OAuth 2.0, sessions, RBAC/ABAC, password security, MFA, and vulnerability prevention. Use when implementing login flows, protecting routes, managing tokens, or auditing auth security.
Scanned 9/7/2026
Install to Claude Code
npx -y skills add modbender/skill-library-mcp --skill auth-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Auth Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/modbender-auth-patterns)More formats (shields.io, HTML) on the badges page.
---
name: auth-patterns
model: standard
description: Authentication and authorization patterns — JWT, OAuth 2.0, sessions, RBAC/ABAC, password security, MFA, and vulnerability prevention. Use when implementing login flows, protecting routes, managing tokens, or auditing auth security.
---
# Auth Patterns — Authentication & Authorization
> **SECURITY-CRITICAL SKILL** — Auth is the front door. Get it wrong and nothing else matters.
## Authentication Methods
| Method | How It Works | Best For |
|--------|-------------|----------|
| **JWT** | Signed token sent with each request | SPAs, microservices, mobile APIs |
| **Session-based** | Server stores session, client holds cookie | Traditional web apps, SSR |
| **OAuth 2.0** | Delegated auth via authorization server | "Login with Google/GitHub", API access |
| **API Keys** | Static key sent in header | Internal services, public APIs |
| **Magic Links** | One-time login link via email | Low-friction onboarding, B2C |
| **Passkeys/WebAuthn** | Hardware/biometric challenge-response | High-security apps, passwordless |
---
## JWT Patterns
### Dual-Token Strategy
Short-lived access token + long-lived refresh token:
```
Client → POST /auth/login → Server
Client ← { access_token, refresh_token }
Client → GET /api/data (Authorization: Bearer <access>) → Server
Client ← 401 Expired
Client → POST /auth/refresh { refresh_token } → Server
Client ← { new_access_token, rotated_refresh_token }
```
### Token Structure
```json
{
"header": { "alg": "RS256", "typ": "JWT", "kid": "key-2024-01" },
"payload": {
"sub": "user_abc123",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1700000900,
"iat": 1700000000,
"jti": "unique-token-id",
"roles": ["user"],
"scope": "read:profile write:profile"
}
}
```
### Signing Algorithms
| Algorithm | Type | When to Use |
|-----------|------|-------------|
| **RS256** | Asymmetric (RSA) | Microservices — only auth server holds private key |
| **ES256** | Asymmetric (ECDSA) | Same as RS256, smaller keys and signatures |
| **HS256** | Symmetric | Single-server apps — all verifiers share secret |
Prefer RS256/ES256 in distributed systems.
### Token Storage
| Storage | XSS Safe | CSRF Safe | Recommendation |
|---------|----------|-----------|----------------|
| **httpOnly cookie** | Yes | No (add CSRF token) | **Best for web apps** |
| **localStorage** | No | Yes | Avoid — XSS exposes tokens |
| **In-memory** | Yes | Yes | Good for SPAs, lost on refresh |
```
Set-Cookie: access_token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=900
```
### Expiration Strategy
| Token | Lifetime | Rotation |
|-------|----------|----------|
| **Access token** | 5–15 minutes | Issued on refresh |
| **Refresh token** | 7–30 days | Rotate on every use |
| **ID token** | Match access token | Not refreshed |
---
## OAuth 2.0 Flows
| Flow | Client Type | When to Use |
|------|-------------|-------------|
| **Authorization Code + PKCE** | Public (SPA, mobile) | **Default for all public clients** |
| **Authorization Code** | Confidential (server) | Server-rendered web apps with backend |
| **Client Credentials** | Machine-to-machine | Service-to-service, cron jobs |
| **Device Code** | Input-constrained | Smart TVs, IoT, CLI on headless servers |
> **Implicit flow is deprecated.** Always use Authorization Code + PKCE for public clients.
### PKCE Flow
```
1. Client generates code_verifier (random 43-128 chars)
2. Client computes code_challenge = BASE64URL(SHA256(code_verifier))
3. Redirect to /authorize?code_challenge=...&code_challenge_method=S256
4. User authenticates, server redirects back with authorization code
5. Client exchanges code + code_verifier for tokens at /token
6. Server verifies SHA256(code_verifier) == code_challenge
```
---
## Session Management
### Server-Side Sessions
```
Client Cookie: session_id=a1b2c3d4 (opaque, random, no user data)
Server Store: { "a1b2c3d4": { userId: 123, roles: ["admin"], expiresAt: ... } }
```
| Store | Speed | When to Use |
|-------|-------|-------------|
| **Redis** | Fast | Production default — TTL support, horizontal scaling |
| **PostgreSQL** | Moderate | When Redis is overkill, need audit trail |
| **In-memory** | Fastest | Development only |
### Session Security
| Threat | Prevention |
|--------|------------|
| Session fixation | Regenerate session ID after login |
| Session hijacking | httpOnly + Secure cookies, bind to IP/user-agent |
| CSRF | SameSite cookies + CSRF tokens |
| Idle timeout | Expire after 15–30 min inactivity |
| Absolute timeout | Force re-auth after 8–24 hours |
---
## Authorization Patterns
| Pattern | Granularity | When to Use |
|---------|-------------|-------------|
| **RBAC** | Coarse (admin, editor, viewer) | Most apps — simple role hierarchy |
| **ABAC** | Fine (attributes: dept, time, location) | Enterprise — context-dependent access |
| **Permission-based** | Medium (post:create, user:delete) | APIs — decouple permissions from roles |
| **Policy-based (OPA/Cedar)** | Fine | Microservices — externalized, auditable rules |
| **ReBAC** | Fine (owner, member, shared-with) | Social apps, Google Drive-style sharing |
### RBAC Implementation
```typescript
const ROLE_PERMISSIONS: Record<string, string[]> = {
admin: ["user:read", "user:write", "user:delete", "post:read", "post:write", "post:delete"],
editor: ["user:read", "post:read", "post:write"],
viewer: ["user:read", "post:read"],
};
function requirePermission(permission: string) {
return (req: Request, res: Response, next: NextFunction) => {
const permissions = ROLE_PERMISSIONS[req.user.role] ?? [];
if (!permissions.includes(permission)) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
}
app.delete("/api/users/:id", requirePermission("user:delete"), deleteUser);
```
---
## Password Security
| Algorithm | Recommended | Memory-Hard | Notes |
|-----------|------------|-------------|-------|
| **Argon2id** | **First choice** | Yes | Resists GPU/ASIC attacks |
| **bcrypt** | Yes | No | Battle-tested, 72-byte limit |
| **scrypt** | Yes | Yes | Good alternative |
| **PBKDF2** | Acceptable | No | NIST approved, weaker vs GPU |
| **SHA-256/MD5** | **Never** | No | Not password hashing |
**NIST 800-63B:** Favor length (12+ chars) over complexity rules. Check against breached password lists. Don't force periodic rotation unless breach suspected.
---
## Multi-Factor Authentication
| Factor | Security | Notes |
|--------|----------|-------|
| **TOTP (Authenticator app)** | High | Offline-capable, Google Authenticator / Authy |
| **WebAuthn/Passkeys** | Highest | Phishing-resistant, hardware-backed |
| **SMS OTP** | Medium | Vulnerable to SIM swap — avoid for high-security |
| **Hardware keys (FIDO2)** | Highest | YubiKey — best for admin accounts |
| **Backup codes** | Low (fallback) | One-time use, generate 10, store hashed |
---
## Security Headers
| Header | Value |
|--------|-------|
| **Strict-Transport-Security** | `max-age=63072000; includeSubDomains; preload` |
| **Content-Security-Policy** | Restrict script sources, no inline scripts |
| **X-Content-Type-Options** | `nosniff` |
| **X-Frame-Options** | `DENY` |
| **Referrer-Policy** | `strict-origin-when-cross-origin` |
| **CORS** | Whitelist specific origins, never `*` with credentials |
---
## Common Vulnerabilities
| # | Vulnerability | Prevention |
|---|--------------|------------|
| 1 | Broken authentication | MFA, strong password policy, breach detection |
| 2 | Session fixation | Regenerate session ID on login |
| 3 | JWT `alg:none` attack | Reject `none`, validate `alg` against allowlist |
| 4 | JWT secret brute force | Use RS256/ES256, strong secrets (256+ bits) |
| 5 | CSRF | SameSite cookies, CSRF tokens |
| 6 | Credential stuffing | Rate limiting, breached password check, MFA |
| 7 | Insecure password storage | Argon2id/bcrypt, never encrypt (hash instead) |
| 8 | Insecure password reset | Signed time-limited tokens, invalidate after use |
| 9 | Open redirect | Validate redirect URIs against allowlist |
| 10 | Token leakage in URL | Send tokens in headers or httpOnly cookies only |
| 11 | Privilege escalation | Server-side role checks on every request |
| 12 | OAuth redirect_uri mismatch | Exact match redirect URI validation, no wildcards |
| 13 | Timing attacks | Constant-time comparison for secrets |
---
## NEVER Do
| # | Rule | Why |
|---|------|-----|
| 1 | **NEVER store passwords in plaintext or reversible encryption** | One breach exposes every user |
| 2 | **NEVER put tokens in URLs or query parameters** | Logged by servers, proxies, referrer headers |
| 3 | **NEVER use `alg: none` or allow algorithm switching in JWTs** | Attacker forges tokens |
| 4 | **NEVER trust client-side role/permission claims** | Users can modify any client-side value |
| 5 | **NEVER use MD5, SHA-1, or plain SHA-256 for password hashing** | No salt, no work factor — cracked in seconds |
| 6 | **NEVER skip HTTPS in production** | Tokens and credentials sent in cleartext |
| 7 | **NEVER log tokens, passwords, or secrets** | Logs are broadly accessible and retained |
| 8 | **NEVER use long-lived tokens without rotation** | A single leak grants indefinite access |
| 9 | **NEVER implement your own crypto** | Use established libraries — jose, bcrypt, passport |
| 10 | **NEVER return different errors for "user not found" vs "wrong password"** | Enables user enumeration |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!