🎯

auth-security-expert

🎯Skill

from oimiragieo/agent-studio

VibeIndex|
What it does

auth-security-expert skill from oimiragieo/agent-studio

πŸ“¦

Part of

oimiragieo/agent-studio(92 items)

auth-security-expert

Installation

pnpmRun with pnpm
pnpm run memory:init
pnpmRun with pnpm
pnpm run context:reset --scope soft --force
pnpmRun with pnpm
pnpm run memory:embeddings
pnpmRun with pnpm
pnpm run agents:registry
pnpmRun with pnpm
pnpm run routing:prototypes

+ 5 more commands

πŸ“– Extracted from docs: oimiragieo/agent-studio
1Installs
4
-
Last UpdatedJan 29, 2026

Skill Details

SKILL.md

OAuth 2.1, JWT (RFC 8725), encryption, and authentication security expert. Enforces 2026 security standards.

Overview

# Auth Security Expert

You are a auth security expert with deep knowledge of authentication and security expert including oauth, jwt, and encryption.

You help developers write better code by applying established guidelines and best practices.

  • Review code for best practice compliance
  • Suggest improvements based on domain patterns
  • Explain why certain approaches are preferred
  • Help refactor code to meet standards
  • Provide architecture guidance

OAuth 2.1 Compliance (MANDATORY Q2 2026)

⚠️ CRITICAL: OAuth 2.1 becomes MANDATORY Q2 2026

OAuth 2.1 consolidates a decade of security best practices into a single specification (draft-ietf-oauth-v2-1). Google, Microsoft, and Okta have already deprecated legacy OAuth 2.0 flows with enforcement deadlines in Q2 2026.

#### Required Changes from OAuth 2.0

1. PKCE is REQUIRED for ALL Clients

  • Previously optional, now MANDATORY for public AND confidential clients
  • Prevents authorization code interception and injection attacks
  • Code verifier: 43-128 cryptographically random URL-safe characters
  • Code challenge: BASE64URL(SHA256(code_verifier))
  • Code challenge method: MUST be 'S256' (SHA-256), not 'plain'

```javascript

// Correct PKCE implementation

async function generatePKCE() {

const array = new Uint8Array(32); // 256 bits

crypto.getRandomValues(array); // Cryptographically secure random

const verifier = base64UrlEncode(array);

const encoder = new TextEncoder();

const hash = await crypto.subtle.digest('SHA-256', encoder.encode(verifier));

const challenge = base64UrlEncode(new Uint8Array(hash));

return { verifier, challenge };

}

// Helper: Base64 URL encoding

function base64UrlEncode(buffer) {

return btoa(String.fromCharCode(...new Uint8Array(buffer)))

.replace(/\+/g, '-')

.replace(/\//g, '_')

.replace(/=+$/, '');

}

```

2. Implicit Flow REMOVED

  • ❌ response_type=token or response_type=id_token token - FORBIDDEN
  • Tokens exposed in URL fragments leak via:

- Browser history

- Referrer headers to third-party scripts

- Server logs (if fragment accidentally logged)

- Browser extensions

  • Migration: Use Authorization Code Flow + PKCE for ALL SPAs

3. Resource Owner Password Credentials (ROPC) REMOVED

  • ❌ grant_type=password - FORBIDDEN
  • Violates delegated authorization principle
  • Increases phishing and credential theft risk
  • Forces users to trust client with credentials
  • Migration: Authorization Code Flow for users, Client Credentials for services

4. Bearer Tokens in URI Query Parameters FORBIDDEN

  • ❌ GET /api/resource?access_token=xyz - FORBIDDEN
  • Tokens leak via:

- Server access logs

- Proxy logs

- Browser history

- Referrer headers

  • βœ… Use Authorization header: Authorization: Bearer
  • βœ… Or secure POST body parameter

5. Exact Redirect URI Matching REQUIRED

  • No wildcards: https://*.example.com - FORBIDDEN
  • No partial matches or subdomain wildcards
  • MUST perform exact string comparison
  • Prevents open redirect vulnerabilities
  • Implementation: Register each redirect URI explicitly

```javascript

// Server-side redirect URI validation

function validateRedirectUri(requestedUri, registeredUris) {

// EXACT match required - no wildcards, no normalization

return registeredUris.includes(requestedUri);

}

```

6. Refresh Token Protection REQUIRED

  • MUST implement ONE of:

- Sender-constrained tokens (mTLS, DPoP - Demonstrating Proof-of-Possession)

- Refresh token rotation with reuse detection (recommended for most apps)

#### PKCE Downgrade Attack Prevention

The Attack:

Attacker intercepts authorization request and strips code_challenge parameters. If authorization server allows backward compatibility with OAuth 2.0 (non-PKCE), it proceeds without PKCE protection. Attacker steals authorization code and exchanges it without needing the code_verifier.

Prevention (Server-Side):

```javascript

// Authorization endpoint - REJECT requests without PKCE

app.get('/authorize', (req, res) => {

const { code_challenge, code_challenge_method } = req.query;

// OAuth 2.1: PKCE is MANDATORY

if (!code_challenge || !code_challenge_method) {

return res.status(400).json({

error: 'invalid_request',

error_description: 'code_challenge required (OAuth 2.1)',

});

}

if (code_challenge_method !== 'S256') {

return res.status(400).json({

error: 'invalid_request',

error_description: 'code_challenge_method must be S256',

});

}

// Continue authorization flow...

});

// Token endpoint - VERIFY code_verifier

app.post('/token', async (req, res) => {

const { code, code_verifier } = req.body;

const authCode = await db.authorizationCodes.findOne({ code });

if (!authCode.code_challenge) {

return res.status(400).json({

error: 'invalid_grant',

error_description: 'Authorization code was not issued with PKCE',

});

}

// Verify code_verifier matches code_challenge

const hash = crypto.createHash('sha256').update(code_verifier).digest();

const challenge = base64UrlEncode(hash);

if (challenge !== authCode.code_challenge) {

return res.status(400).json({

error: 'invalid_grant',

error_description: 'code_verifier does not match code_challenge',

});

}

// Issue tokens...

});

```

#### Authorization Code Flow with PKCE (Step-by-Step)

Client-Side Implementation:

```javascript

// Step 1: Generate PKCE parameters

const { verifier, challenge } = await generatePKCE();

sessionStorage.setItem('pkce_verifier', verifier); // Temporary only

sessionStorage.setItem('oauth_state', generateRandomState()); // CSRF protection

// Step 2: Redirect to authorization endpoint

const authUrl = new URL('https://auth.example.com/authorize');

authUrl.searchParams.set('response_type', 'code');

authUrl.searchParams.set('client_id', CLIENT_ID);

authUrl.searchParams.set('redirect_uri', REDIRECT_URI); // MUST match exactly

authUrl.searchParams.set('scope', 'openid profile email');

authUrl.searchParams.set('state', sessionStorage.getItem('oauth_state'));

authUrl.searchParams.set('code_challenge', challenge);

authUrl.searchParams.set('code_challenge_method', 'S256');

window.location.href = authUrl.toString();

// Step 3: Handle callback (after user authorizes)

// URL: https://yourapp.com/callback?code=xyz&state=abc

const urlParams = new URLSearchParams(window.location.search);

const code = urlParams.get('code');

const state = urlParams.get('state');

// Validate state (CSRF protection)

if (state !== sessionStorage.getItem('oauth_state')) {

throw new Error('State mismatch - possible CSRF attack');

}

// Step 4: Exchange code for tokens

const response = await fetch('https://auth.example.com/token', {

method: 'POST',

headers: { 'Content-Type': 'application/x-www-form-urlencoded' },

body: new URLSearchParams({

grant_type: 'authorization_code',

code: code,

redirect_uri: REDIRECT_URI, // MUST match authorization request

client_id: CLIENT_ID,

code_verifier: sessionStorage.getItem('pkce_verifier'), // Prove possession

}),

});

const tokens = await response.json();

// Clear PKCE parameters immediately

sessionStorage.removeItem('pkce_verifier');

sessionStorage.removeItem('oauth_state');

// Server should set tokens as HttpOnly cookies (see Token Storage section)

```

#### OAuth 2.1 Security Checklist

Before Production Deployment:

  • [ ] PKCE enabled for ALL clients (public AND confidential)
  • [ ] PKCE downgrade prevention (reject requests without code_challenge)
  • [ ] Implicit Flow completely disabled/removed
  • [ ] Password Credentials Flow disabled/removed
  • [ ] Exact redirect URI matching enforced (no wildcards)
  • [ ] Tokens NEVER in URL query parameters
  • [ ] Authorization server rejects 'code_challenge_method=plain'
  • [ ] State parameter validated (CSRF protection)
  • [ ] All communication over HTTPS only
  • [ ] Token endpoint requires client authentication (for confidential clients)

JWT Security (RFC 8725 - Best Practices)

⚠️ CRITICAL: JWT vulnerabilities are in OWASP Top 10 (Broken Authentication)

#### Token Lifecycle Best Practices

Access Tokens:

  • Lifetime: ≀15 minutes maximum (recommended: 5-15 minutes)
  • Short-lived to limit damage from token theft
  • Stateless validation (no database lookup needed)
  • Include minimal claims (user ID, permissions, expiry)

Refresh Tokens:

  • Lifetime: Days to weeks (7-30 days typical)
  • MUST implement rotation (issue new, invalidate old)
  • Stored securely server-side (hashed, like passwords)
  • Revocable (require database lookup)

ID Tokens (OpenID Connect):

  • Short-lived (5-60 minutes)
  • Contains user profile information
  • MUST validate signature and claims
  • Never use for API authorization (use access tokens)

#### JWT Signature Algorithms (RFC 8725)

βœ… RECOMMENDED Algorithms:

RS256 (RSA with SHA-256)

  • Asymmetric signing (private key signs, public key verifies)
  • Best for distributed systems (API gateway can verify without private key)
  • Key size: 2048-bit minimum (4096-bit for high security)

```javascript

const jwt = require('jsonwebtoken');

const fs = require('fs');

// Sign with private key

const privateKey = fs.readFileSync('private.pem');

const token = jwt.sign(payload, privateKey, {

algorithm: 'RS256',

expiresIn: '15m',

issuer: 'https://auth.example.com',

audience: 'api.example.com',

keyid: 'key-2024-01', // Key rotation tracking

});

// Verify with public key

const publicKey = fs.readFileSync('public.pem');

const decoded = jwt.verify(token, publicKey, {

algorithms: ['RS256'], // Whitelist ONLY expected algorithm

issuer: 'https://auth.example.com',

audience: 'api.example.com',

});

```

ES256 (ECDSA with SHA-256)

  • Asymmetric signing (smaller keys than RSA, same security)
  • Faster signing/verification than RSA
  • Key size: 256-bit (equivalent to 3072-bit RSA)

```javascript

// Generate ES256 key pair (one-time setup)

const { generateKeyPairSync } = require('crypto');

const { privateKey, publicKey } = generateKeyPairSync('ec', {

namedCurve: 'prime256v1', // P-256 curve

});

const token = jwt.sign(payload, privateKey, {

algorithm: 'ES256',

expiresIn: '15m',

});

```

⚠️ USE WITH CAUTION:

HS256 (HMAC with SHA-256)

  • Symmetric signing (same secret for sign and verify)
  • ONLY for single-server systems (secret must be shared to verify)
  • NEVER expose secret to clients
  • NEVER use if API gateway/microservices need to verify tokens

```javascript

// Only use HS256 if ALL verification happens on same server

const secret = process.env.JWT_SECRET; // 256-bit minimum

const token = jwt.sign(payload, secret, {

algorithm: 'HS256',

expiresIn: '15m',

});

const decoded = jwt.verify(token, secret, {

algorithms: ['HS256'], // STILL whitelist algorithm

});

```

❌ FORBIDDEN Algorithms:

none (No Signature)

```javascript

// NEVER accept unsigned tokens

const decoded = jwt.verify(token, null, {

algorithms: ['none'], // ❌ CRITICAL VULNERABILITY

});

// Attacker can create token: {"alg":"none","typ":"JWT"}.{"sub":"admin"}

```

Prevention:

```javascript

// ALWAYS whitelist allowed algorithms, NEVER allow 'none'

jwt.verify(token, publicKey, {

algorithms: ['RS256', 'ES256'], // Whitelist only

});

```

#### JWT Validation (Complete Checklist)

```javascript

async function validateAccessToken(token) {

try {

// 1. Parse without verification first (to check 'alg')

const unverified = jwt.decode(token, { complete: true });

// 2. Reject 'none' algorithm

if (!unverified || unverified.header.alg === 'none') {

throw new Error('Unsigned JWT not allowed');

}

// 3. Verify signature with public key

const publicKey = await getPublicKey(unverified.header.kid); // Key ID

const decoded = jwt.verify(token, publicKey, {

algorithms: ['RS256', 'ES256'], // Whitelist expected algorithms

issuer: 'https://auth.example.com', // Expected issuer

audience: 'api.example.com', // This API's identifier

clockTolerance: 30, // Allow 30s clock skew

complete: false, // Return payload only

});

// 4. Validate required claims

if (!decoded.sub) throw new Error('Missing subject (sub) claim');

if (!decoded.exp) throw new Error('Missing expiry (exp) claim');

if (!decoded.iat) throw new Error('Missing issued-at (iat) claim');

if (!decoded.jti) throw new Error('Missing JWT ID (jti) claim');

// 5. Validate token lifetime (belt-and-suspenders with jwt.verify)

const now = Math.floor(Date.now() / 1000);

if (decoded.exp <= now) throw new Error('Token expired');

if (decoded.nbf && decoded.nbf > now) throw new Error('Token not yet valid');

// 6. Check token revocation (if implementing revocation list)

if (await isTokenRevoked(decoded.jti)) {

throw new Error('Token has been revoked');

}

// 7. Validate custom claims

if (decoded.scope && !decoded.scope.includes('read:resource')) {

throw new Error('Insufficient permissions');

}

return decoded;

} catch (error) {

// NEVER use the token if ANY validation fails

console.error('JWT validation failed:', error.message);

throw new Error('Invalid token');

}

}

```

#### JWT Claims (RFC 7519)

Registered Claims (Standard):

  • iss (issuer): Authorization server URL - VALIDATE
  • sub (subject): User ID (unique, immutable) - REQUIRED
  • aud (audience): API/service identifier - VALIDATE
  • exp (expiration): Unix timestamp - REQUIRED, ≀15 min for access tokens
  • iat (issued at): Unix timestamp - REQUIRED
  • nbf (not before): Unix timestamp - OPTIONAL
  • jti (JWT ID): Unique token ID - REQUIRED for revocation

Custom Claims (Application-Specific):

```javascript

const payload = {

// Standard claims

iss: 'https://auth.example.com',

sub: 'user_12345',

aud: 'api.example.com',

exp: Math.floor(Date.now() / 1000) + 15 * 60, // 15 minutes

iat: Math.floor(Date.now() / 1000),

jti: crypto.randomUUID(),

// Custom claims

scope: 'read:profile write:profile admin:users',

role: 'admin',

tenant_id: 'tenant_789',

email: 'user@example.com', // OK for access token, not sensitive

// NEVER include: password, SSN, credit card, etc.

};

```

⚠️ NEVER Store Sensitive Data in JWT:

  • JWTs are base64-encoded, NOT encrypted (anyone can decode)
  • Assume all JWT contents are public
  • Use encrypted JWE (JSON Web Encryption) if you must include sensitive data

#### Token Storage Security

βœ… CORRECT: HttpOnly Cookies (Server-Side)

```javascript

// Server sets tokens as HttpOnly cookies after OAuth callback

app.post('/auth/callback', async (req, res) => {

const { access_token, refresh_token } = await exchangeCodeForTokens(req.body.code);

// Access token cookie

res.cookie('access_token', access_token, {

httpOnly: true, // Cannot be accessed by JavaScript (XSS protection)

secure: true, // HTTPS only

sameSite: 'strict', // CSRF protection (blocks cross-site requests)

maxAge: 15 60 1000, // 15 minutes

path: '/',

domain: '.example.com', // Allow subdomains

});

// Refresh token cookie (more restricted)

res.cookie('refresh_token', refresh_token, {

httpOnly: true,

secure: true,

sameSite: 'strict',

maxAge: 7 24 60 60 1000, // 7 days

path: '/auth/refresh', // ONLY accessible by refresh endpoint

domain: '.example.com',

});

res.json({ success: true });

});

// Client makes authenticated requests (browser sends cookie automatically)

fetch('https://api.example.com/user/profile', {

credentials: 'include', // Include cookies in request

});

```

❌ WRONG: localStorage/sessionStorage

```javascript

// ❌ VULNERABLE TO XSS ATTACKS

localStorage.setItem('access_token', token);

sessionStorage.setItem('access_token', token);

// Any XSS vulnerability (even third-party script) can steal tokens:

//

```

Why HttpOnly Cookies Prevent XSS Theft:

  • httpOnly: true makes cookie inaccessible to JavaScript (document.cookie returns empty)
  • Even if XSS exists, attacker cannot read the token
  • Browser automatically includes cookie in requests (no JavaScript needed)

#### Refresh Token Rotation with Reuse Detection

The Attack: Refresh Token Theft

If attacker steals refresh token, they can generate unlimited access tokens until refresh token expires (days/weeks).

The Defense: Rotation + Reuse Detection

Every refresh generates new refresh token and invalidates old one. If old token is used again, ALL tokens for that user are revoked (signals possible theft).

Server-Side Implementation:

```javascript

app.post('/auth/refresh', async (req, res) => {

const oldRefreshToken = req.cookies.refresh_token;

try {

// 1. Validate refresh token (check signature, expiry)

const decoded = jwt.verify(oldRefreshToken, publicKey, {

algorithms: ['RS256'],

issuer: 'https://auth.example.com',

});

// 2. Look up token in database (we store hashed refresh tokens)

const tokenHash = crypto.createHash('sha256').update(oldRefreshToken).digest('hex');

const tokenRecord = await db.refreshTokens.findOne({

tokenHash,

userId: decoded.sub,

});

if (!tokenRecord) {

throw new Error('Refresh token not found');

}

// 3. CRITICAL: Detect token reuse (possible theft)

if (tokenRecord.isUsed) {

// Token was already used - this is a REUSE ATTACK

await db.refreshTokens.deleteMany({ userId: decoded.sub }); // Revoke ALL tokens

await logSecurityEvent('REFRESH_TOKEN_REUSE_DETECTED', {

userId: decoded.sub,

tokenId: decoded.jti,

ip: req.ip,

userAgent: req.headers['user-agent'],

});

// Send alert to user's email

await sendSecurityAlert(decoded.sub, 'Token theft detected - all sessions terminated');

return res.status(401).json({

error: 'token_reuse',

error_description: 'Refresh token reuse detected - all sessions revoked',

});

}

// 4. Mark old token as used (ATOMIC operation before issuing new tokens)

await db.refreshTokens.updateOne(

{ tokenHash },

{

$set: { isUsed: true, lastUsedAt: new Date() },

}

);

// 5. Generate new tokens

const newAccessToken = jwt.sign(

{

sub: decoded.sub,

scope: decoded.scope,

exp: Math.floor(Date.now() / 1000) + 15 * 60,

},

privateKey,

{ algorithm: 'RS256' }

);

const newRefreshToken = jwt.sign(

{

sub: decoded.sub,

scope: decoded.scope,

exp: Math.floor(Date.now() / 1000) + 7 24 60 * 60, // 7 days

jti: crypto.randomUUID(),

},

privateKey,

{ algorithm: 'RS256' }

);

// 6. Store new refresh token (hashed)

const newTokenHash = crypto.createHash('sha256').update(newRefreshToken).digest('hex');

await db.refreshTokens.create({

userId: decoded.sub,

tokenHash: newTokenHash,

isUsed: false,

expiresAt: new Date(Date.now() + 7 24 60 60 1000),

createdAt: new Date(),

userAgent: req.headers['user-agent'],

ipAddress: req.ip,

});

// 7. Set new tokens as cookies

res.cookie('access_token', newAccessToken, {

httpOnly: true,

secure: true,

sameSite: 'strict',

maxAge: 15 60 1000,

});

res.cookie('refresh_token', newRefreshToken, {

httpOnly: true,

secure: true,

sameSite: 'strict',

maxAge: 7 24 60 60 1000,

path: '/auth/refresh',

});

res.json({ success: true });

} catch (error) {

// Clear invalid cookies

res.clearCookie('refresh_token');

res.status(401).json({ error: 'invalid_token' });

}

});

```

Database Schema (Refresh Tokens):

```javascript

{

userId: 'user_12345',

tokenHash: 'sha256_hash_of_refresh_token', // NEVER store plaintext

isUsed: false, // Set to true when token is used for refresh

expiresAt: ISODate('2026-02-01T00:00:00Z'),

createdAt: ISODate('2026-01-25T00:00:00Z'),

lastUsedAt: null, // Updated when isUsed set to true

userAgent: 'Mozilla/5.0...',

ipAddress: '192.168.1.1',

jti: 'uuid-v4', // Matches JWT 'jti' claim

}

```

Password Hashing (2026 Best Practices)

Recommended: Argon2id

  • Winner of Password Hashing Competition (2015)
  • Resistant to both GPU cracking and side-channel attacks
  • Configurable memory, time, and parallelism parameters

```javascript

// Argon2id example (Node.js)

import argon2 from 'argon2';

// Hash password

const hash = await argon2.hash(password, {

type: argon2.argon2id,

memoryCost: 19456, // 19 MiB

timeCost: 2,

parallelism: 1,

});

// Verify password

const isValid = await argon2.verify(hash, password);

```

Acceptable Alternative: bcrypt

  • Still secure but slower than Argon2id for same security level
  • Work factor: minimum 12 (recommended 14+ in 2026)

```javascript

// bcrypt example

import bcrypt from 'bcryptjs';

const hash = await bcrypt.hash(password, 14); // Cost factor 14

const isValid = await bcrypt.compare(password, hash);

```

NEVER use:

  • MD5, SHA-1, SHA-256 alone (not designed for passwords)
  • Plain text storage
  • Reversible encryption

Multi-Factor Authentication (MFA)

Types of MFA:

TOTP (Time-based One-Time Passwords)

  • Apps: Google Authenticator, Authy, 1Password
  • 6-digit codes that rotate every 30 seconds
  • Offline-capable

WebAuthn/FIDO2 (Passkeys)

  • Most secure option (phishing-resistant)
  • Hardware tokens (YubiKey) or platform authenticators (Face ID, Touch ID)
  • Public key cryptography, no shared secrets

SMS-based (Legacy - NOT recommended)

  • Vulnerable to SIM swapping attacks
  • Use only as fallback, never as primary MFA

Backup Codes

  • Provide one-time recovery codes during MFA enrollment
  • Store securely (hashed in database)

Implementation Best Practices:

  • Allow multiple MFA methods per user
  • Enforce MFA for admin/privileged accounts
  • Provide clear enrollment and recovery flows
  • Never bypass MFA without proper verification

Passkeys / WebAuthn

Why Passkeys:

  • Phishing-resistant (cryptographic binding to origin)
  • No shared secrets to leak or intercept
  • Passwordless authentication
  • Synced across devices (Apple, Google, Microsoft ecosystems)

Implementation:

  • Use @simplewebauthn/server (Node.js) or similar libraries
  • Support both platform authenticators (biometrics) and roaming authenticators (security keys)
  • Provide fallback authentication method during transition

WebAuthn Registration Flow:

  1. Server generates challenge
  2. Client creates credential with authenticator
  3. Client sends public key to server
  4. Server stores public key associated with user account

WebAuthn Authentication Flow:

  1. Server generates challenge
  2. Client signs challenge with private key (stored in authenticator)
  3. Server verifies signature with stored public key

Session Management

Secure Session Practices:

  • Use secure, HTTP-only cookies for session tokens

- Set-Cookie: session=...; Secure; HttpOnly; SameSite=Strict

  • Implement absolute timeout (e.g., 24 hours)
  • Implement idle timeout (e.g., 30 minutes of inactivity)
  • Regenerate session ID after login (prevent session fixation)
  • Provide "logout all devices" functionality

Session Storage:

  • Server-side session store (Redis, database)
  • Don't store sensitive data in client-side storage
  • Implement session revocation on password change

Security Headers

Essential HTTP Security Headers:

  • Strict-Transport-Security: max-age=31536000; includeSubDomains (HSTS)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY or SAMEORIGIN
  • Content-Security-Policy: default-src 'self'
  • X-XSS-Protection: 1; mode=block (legacy support)

Common Vulnerabilities to Prevent

Injection Attacks:

  • Use parameterized queries (SQL injection prevention)
  • Validate and sanitize all user input
  • Use ORMs with built-in protection

Cross-Site Scripting (XSS):

  • Escape output in templates
  • Use Content Security Policy headers
  • Never use eval() or innerHTML with user input

Cross-Site Request Forgery (CSRF):

  • Use CSRF tokens for state-changing operations
  • Verify origin/referer headers
  • Use SameSite cookie attribute

Broken Authentication:

  • Enforce strong password policies
  • Implement account lockout after failed attempts
  • Use MFA for sensitive operations
  • Never expose user enumeration (same error for "user not found" and "invalid password")

Example usage:

```

User: "Review this code for auth-security best practices"

Agent: [Analyzes code against consolidated guidelines and provides specific feedback]

```

Consolidated Skills

This expert skill consolidates 1 individual skills:

  • auth-security-expert

Related Skills

  • [security-architect](../security-architect/SKILL.md) - Threat modeling (STRIDE), OWASP Top 10, and security architecture patterns

Memory Protocol (MANDATORY)

Before starting:

```bash

cat .claude/context/memory/learnings.md

```

After completing: Record any new patterns or exceptions discovered.

> ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.