auth-comprehensive
npx skills add https://github.com/majiayu000/claude-skill-registry --skill auth-comprehensive
Agent 安装分布
Skill 文档
Comprehensive Authentication & Authorization
Enterprise-grade authentication system supporting multiple frameworks with intelligent pattern selection, advanced security hardening, and production-ready implementations across all authentication methods (JWT, sessions, cookies, hybrid approaches).
Quick Decision Tree
Choose authentication method based on requirements:
Need stateless, API-first, microservices?
ââ YES â JWT Tokens (access + refresh)
ââ NO â Continue
Need session-based, traditional web app?
ââ YES â Sessions (server-side)
ââ NO â Continue
Need browser cookies, XSS protection?
ââ YES â HTTP-Only Cookies
ââ NO â Continue
Need best of both worlds?
ââ Hybrid (JWT + Cookies)
Need enhanced security & ease?
ââ Better Auth / Auth Libraries
Core Authentication Patterns
Pattern 1: JWT (JSON Web Tokens) – Stateless
Best For: APIs, microservices, mobile apps, single-page applications
# Python/FastAPI
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
class JWTManager:
def __init__(self, secret_key: str, algorithm: str = "HS256"):
self.secret_key = secret_key
self.algorithm = algorithm
self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(self, password: str) -> str:
return self.pwd_context.hash(password)
def verify_password(self, plain: str, hashed: str) -> bool:
return self.pwd_context.verify(plain, hashed)
def create_access_token(self, data: dict, expires_delta: timedelta = None) -> str:
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire, "type": "access"})
return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
def create_refresh_token(self, user_id: int) -> str:
payload = {
"sub": str(user_id),
"type": "refresh",
"exp": datetime.utcnow() + timedelta(days=7)
}
return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
def decode_token(self, token: str) -> dict:
try:
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
return payload
except JWTError:
return None
// JavaScript/Node.js/Express
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
class JWTManager {
constructor(secretKey, algorithm = 'HS256') {
this.secretKey = secretKey;
this.algorithm = algorithm;
}
hashPassword(password) {
return bcrypt.hashSync(password, 10);
}
verifyPassword(plain, hashed) {
return bcrypt.compareSync(plain, hashed);
}
createAccessToken(data, expiresIn = '15m') {
return jwt.sign(
{ ...data, type: 'access' },
this.secretKey,
{ algorithm: this.algorithm, expiresIn }
);
}
createRefreshToken(userId) {
return jwt.sign(
{ sub: userId, type: 'refresh' },
this.secretKey,
{ expiresIn: '7d' }
);
}
decodeToken(token) {
try {
return jwt.verify(token, this.secretKey);
} catch (error) {
return null;
}
}
}
module.exports = JWTManager;
// TypeScript/Next.js
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
interface TokenPayload {
sub: string;
type: 'access' | 'refresh';
iat: number;
exp: number;
}
class JWTManager {
private secretKey: string;
private algorithm: string = 'HS256';
constructor(secretKey: string) {
this.secretKey = secretKey;
}
hashPassword(password: string): string {
return bcrypt.hashSync(password, 10);
}
verifyPassword(plain: string, hashed: string): boolean {
return bcrypt.compareSync(plain, hashed);
}
createAccessToken(data: any, expiresIn: string = '15m'): string {
return jwt.sign(
{ ...data, type: 'access' },
this.secretKey,
{ algorithm: this.algorithm, expiresIn }
);
}
createRefreshToken(userId: number): string {
return jwt.sign(
{ sub: userId, type: 'refresh' },
this.secretKey,
{ expiresIn: '7d' }
);
}
decodeToken(token: string): TokenPayload | null {
try {
return jwt.verify(token, this.secretKey) as TokenPayload;
} catch (error) {
return null;
}
}
}
export default JWTManager;
Pattern 2: Session-Based Authentication
Best For: Traditional web apps, server-rendered applications, CSRF protection needed
# Python/Flask or FastAPI with sessions
from sqlalchemy.orm import Session
from datetime import datetime, timedelta
import secrets
class SessionManager:
def __init__(self, db: Session, session_timeout_minutes: int = 30):
self.db = db
self.timeout = timedelta(minutes=session_timeout_minutes)
def create_session(self, user_id: int) -> str:
"""Create new session and return session ID"""
session_token = secrets.token_urlsafe(32)
session_record = {
'user_id': user_id,
'token': session_token,
'created_at': datetime.utcnow(),
'expires_at': datetime.utcnow() + self.timeout,
'last_activity': datetime.utcnow()
}
# Save to database
self.db.create(UserSession, session_record)
self.db.commit()
return session_token
def get_session(self, token: str) -> dict | None:
"""Retrieve and validate session"""
session = self.db.query(UserSession).filter(
UserSession.token == token,
UserSession.expires_at > datetime.utcnow()
).first()
if session:
# Update last activity
session.last_activity = datetime.utcnow()
self.db.commit()
return {'user_id': session.user_id}
return None
def invalidate_session(self, token: str) -> bool:
"""Logout - invalidate session"""
session = self.db.query(UserSession).filter(
UserSession.token == token
).first()
if session:
self.db.delete(session)
self.db.commit()
return True
return False
// JavaScript/Express with express-session
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const redisClient = createClient();
redisClient.connect();
const sessionMiddleware = session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JS access
sameSite: 'strict',
maxAge: 30 * 60 * 1000 // 30 minutes
}
});
app.use(sessionMiddleware);
app.post('/login', (req, res) => {
if (authenticateUser(req.body)) {
req.session.userId = user.id;
req.session.role = user.role;
res.json({ message: 'Logged in' });
}
});
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) return res.status(500).json({ error: 'Logout failed' });
res.json({ message: 'Logged out' });
});
});
Pattern 3: HTTP-Only Cookies (Browser Security)
Best For: Web applications, maximum XSS protection
// Express/Node.js - Setting HTTP-Only Cookies
app.post('/login', async (req, res) => {
const user = await authenticateUser(req.body.email, req.body.password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
);
// Set HTTP-Only cookie - cannot be accessed by JavaScript
res.cookie('accessToken', accessToken, {
httpOnly: true, // No JS access (prevents XSS)
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 15 * 60 * 1000, // 15 minutes
path: '/',
domain: process.env.COOKIE_DOMAIN
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
path: '/api/auth/refresh'
});
res.json({ message: 'Login successful' });
});
// Middleware to extract token from cookies
function authenticateRequest(req, res, next) {
const token = req.cookies.accessToken;
if (!token) {
return res.status(401).json({ error: 'No token' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userId = decoded.userId;
req.role = decoded.role;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}
app.get('/api/protected', authenticateRequest, (req, res) => {
res.json({ data: 'Protected content', userId: req.userId });
});
// Next.js - Handling cookies securely
import { cookies } from 'next/headers';
import jwt from 'jsonwebtoken';
export async function login(email: string, password: string) {
const user = await authenticateUser(email, password);
if (!user) throw new Error('Invalid credentials');
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
);
const cookieStore = await cookies();
// Set HTTP-Only cookie
cookieStore.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 15 * 60, // seconds
path: '/'
});
cookieStore.set('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60,
path: '/api/auth/refresh'
});
}
export async function getUser() {
const cookieStore = await cookies();
const token = cookieStore.get('accessToken')?.value;
if (!token) return null;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return decoded;
} catch {
return null;
}
}
Pattern 4: Hybrid Approach (JWT + Cookies)
Best For: Maximum security + flexibility, modern web apps
# FastAPI - Hybrid JWT + HTTP-Only Cookies
from fastapi import FastAPI, Cookie, Depends, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/login")
async def login(email: str, password: str):
user = authenticate_user(email, password)
if not user:
raise HTTPException(status_code=401, detail="Invalid credentials")
# Create tokens
access_token = create_access_token({"sub": str(user.id)})
refresh_token = create_refresh_token(user.id)
# Store refresh token in database for revocation
store_refresh_token(user.id, refresh_token)
response = JSONResponse(content={"access_token": access_token})
# Set refresh token in HTTP-Only cookie
response.set_cookie(
key="refresh_token",
value=refresh_token,
httponly=True,
secure=True,
samesite="strict",
max_age=7 * 24 * 60 * 60
)
return response
async def get_current_user(
access_token: str = Cookie(None),
refresh_token: str = Cookie(None)
):
"""Validate access token, refresh if needed"""
if access_token:
try:
payload = jwt.decode(access_token, SECRET_KEY)
return payload["sub"]
except JWTError:
pass
# Try refresh token
if refresh_token:
user_id = verify_refresh_token(refresh_token)
if user_id:
new_access = create_access_token({"sub": str(user_id)})
# Return new access token
return user_id
raise HTTPException(status_code=401, detail="Not authenticated")
@app.get("/protected")
async def protected_route(user_id: str = Depends(get_current_user)):
return {"user_id": user_id}
Multi-Factor Authentication (MFA)
TOTP (Time-based One-Time Password)
# FastAPI with TOTP (Google Authenticator)
from pyotp import TOTP
from qrcode import QRCode
class MFAManager:
@staticmethod
def generate_secret() -> str:
"""Generate TOTP secret"""
return TOTP.new().secret
@staticmethod
def get_provisioning_uri(secret: str, user_email: str, issuer: str) -> str:
"""Get QR code URI for authenticator app"""
totp = TOTP(secret)
return totp.provisioning_uri(name=user_email, issuer_name=issuer)
@staticmethod
def verify_token(secret: str, token: str) -> bool:
"""Verify TOTP token"""
totp = TOTP(secret)
return totp.verify(token)
@app.post("/auth/mfa/setup")
async def setup_mfa(current_user: User = Depends(get_current_user)):
"""Generate MFA secret and QR code"""
secret = MFAManager.generate_secret()
uri = MFAManager.get_provisioning_uri(secret, current_user.email, "MyApp")
# Return QR code and secret
return {
"secret": secret,
"qr_code_uri": uri
}
@app.post("/auth/mfa/enable")
async def enable_mfa(
mfa_token: str,
mfa_secret: str,
current_user: User = Depends(get_current_user)
):
"""Enable MFA after verification"""
if not MFAManager.verify_token(mfa_secret, mfa_token):
raise HTTPException(status_code=400, detail="Invalid MFA token")
# Save secret to database
update_user_mfa(current_user.id, mfa_secret)
return {"message": "MFA enabled"}
@app.post("/auth/login-mfa")
async def login_with_mfa(email: str, password: str, mfa_token: str):
"""Login with MFA verification"""
user = authenticate_user(email, password)
if not user:
raise HTTPException(status_code=401)
if not MFAManager.verify_token(user.mfa_secret, mfa_token):
raise HTTPException(status_code=401, detail="Invalid MFA token")
access_token = create_access_token({"sub": str(user.id)})
return {"access_token": access_token}
Email Verification
# FastAPI with Email Verification
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
import secrets
class EmailVerificationManager:
@staticmethod
def create_verification_token(user_id: int) -> str:
token = secrets.token_urlsafe(32)
store_verification_token(user_id, token)
return token
@staticmethod
def send_verification_email(email: str, token: str):
verification_link = f"https://yourapp.com/auth/verify?token={token}"
message = Mail(
from_email="noreply@yourapp.com",
to_emails=email,
subject="Verify your email",
html_content=f'<a href="{verification_link}">Click to verify</a>'
)
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
sg.send(message)
@staticmethod
def verify_email(token: str) -> int | None:
user_id = get_user_id_from_token(token)
if user_id and is_token_valid(token):
mark_email_verified(user_id)
delete_verification_token(token)
return user_id
return None
@app.post("/auth/register")
async def register(email: str, password: str):
user = create_user(email, hash_password(password))
token = EmailVerificationManager.create_verification_token(user.id)
EmailVerificationManager.send_verification_email(email, token)
return {"message": "Verification email sent"}
@app.get("/auth/verify")
async def verify_email(token: str):
user_id = EmailVerificationManager.verify_email(token)
if user_id:
return {"message": "Email verified"}
raise HTTPException(status_code=400, detail="Invalid token")
OAuth2 & Social Login
Google OAuth2
# FastAPI with Google OAuth2
from google.auth.transport import requests
from google.oauth2 import id_token
class GoogleOAuth:
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
@staticmethod
def verify_token(token: str) -> dict | None:
try:
idinfo = id_token.verify_oauth2_token(
token,
requests.Request(),
GoogleOAuth.GOOGLE_CLIENT_ID
)
return idinfo
except ValueError:
return None
@app.post("/auth/google")
async def google_login(token: str):
idinfo = GoogleOAuth.verify_token(token)
if not idinfo:
raise HTTPException(status_code=400, detail="Invalid token")
# Create or get user
user = get_or_create_user(
email=idinfo["email"],
name=idinfo["name"],
picture=idinfo["picture"]
)
access_token = create_access_token({"sub": str(user.id)})
return {"access_token": access_token, "user": user}
// Next.js with Google OAuth2
import GoogleProvider from "next-auth/providers/google";
import { NextAuthOptions } from "next-auth";
export const authOptions: NextAuthOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
})
],
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
session.user.id = token.sub;
return session;
}
}
};
Role-Based Access Control (RBAC)
# FastAPI RBAC
from enum import Enum
class Role(str, Enum):
ADMIN = "admin"
MODERATOR = "moderator"
USER = "user"
class Permission(str, Enum):
READ = "read"
WRITE = "write"
DELETE = "delete"
MANAGE_USERS = "manage_users"
ROLE_PERMISSIONS = {
Role.ADMIN: [Permission.READ, Permission.WRITE, Permission.DELETE, Permission.MANAGE_USERS],
Role.MODERATOR: [Permission.READ, Permission.WRITE, Permission.DELETE],
Role.USER: [Permission.READ]
}
def has_permission(required_permission: Permission):
async def permission_checker(current_user: User = Depends(get_current_user)):
user_permissions = ROLE_PERMISSIONS.get(current_user.role, [])
if required_permission not in user_permissions:
raise HTTPException(status_code=403, detail="Permission denied")
return current_user
return permission_checker
@app.delete("/users/{user_id}")
async def delete_user(
user_id: int,
current_user: User = Depends(has_permission(Permission.MANAGE_USERS))
):
delete_user_from_db(user_id)
return {"message": "User deleted"}
Password Hashing & Security
Bcrypt
# Python/FastAPI
from passlib.context import CryptContext
pwd_context = CryptContext(
schemes=["bcrypt"],
deprecated="auto",
bcrypt__rounds=12 # Cost factor - higher = more secure but slower
)
def hash_password(password: str) -> str:
# Ensure password is at least 8 chars, has uppercase, digit, special char
if len(password) < 8:
raise ValueError("Password must be at least 8 characters")
if not any(c.isupper() for c in password):
raise ValueError("Password must contain uppercase letter")
if not any(c.isdigit() for c in password):
raise ValueError("Password must contain digit")
if not any(c in "!@#$%^&*.-_" for c in password):
raise ValueError("Password must contain special character")
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
// JavaScript/Node.js
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
async function hashPassword(password) {
// Validate password strength
const strongRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
if (!strongRegex.test(password)) {
throw new Error('Password must be 8+ chars with uppercase, digit, and special char');
}
return bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(plainPassword, hashedPassword) {
return bcrypt.compare(plainPassword, hashedPassword);
}
Better Auth Integration
Better Auth is a modern authentication library that simplifies many patterns:
// Next.js with Better Auth
import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";
export const auth = betterAuth({
database: prisma,
secret: process.env.BETTER_AUTH_SECRET,
plugins: [
nextCookies()
],
user: {
additionalFields: {
role: {
type: "string",
required: false,
defaultValue: "user"
}
}
},
emailVerification: {
sendVerificationEmail: async ({ email, url }) => {
await sendEmail({
to: email,
subject: "Verify your email",
html: `<a href="${url}">Verify email</a>`
});
}
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET
},
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET
}
}
});
// Usage in Next.js
export { auth as default } from '@/auth';
// Client-side
import { signIn, signOut, useSession } from "better-auth/client";
export function LoginButton() {
return (
<button onClick={() => signIn.social({ provider: "google" })}>
Login with Google
</button>
);
}
Token Revocation & Logout
# FastAPI - Token Revocation (Blacklist)
from redis import Redis
redis_client = Redis(host='localhost', port=6379, db=0)
class TokenRevocationManager:
@staticmethod
def revoke_token(token: str, ttl_seconds: int):
"""Add token to blacklist"""
redis_client.setex(f"revoked_token:{token}", ttl_seconds, "1")
@staticmethod
def is_revoked(token: str) -> bool:
"""Check if token is revoked"""
return redis_client.exists(f"revoked_token:{token}") > 0
async def get_current_user(token: str = Depends(oauth2_scheme)):
if TokenRevocationManager.is_revoked(token):
raise HTTPException(status_code=401, detail="Token revoked")
payload = jwt.decode(token, SECRET_KEY)
return payload["sub"]
@app.post("/auth/logout")
async def logout(current_user: User = Depends(get_current_user), token: str = Header()):
# Revoke token
payload = jwt.decode(token, SECRET_KEY)
remaining_time = payload["exp"] - datetime.utcnow().timestamp()
TokenRevocationManager.revoke_token(token, int(remaining_time))
return {"message": "Logged out"}
Security Hardening Checklist
â Password Security
- Minimum 8 characters
- Uppercase + lowercase
- Numbers and special characters
- Bcrypt with cost factor 12+
- Rate limiting on login attempts
â Token Security
- Short expiration times (15-30 minutes)
- Refresh token rotation
- Token revocation support
- Secure storage (HTTP-Only cookies)
â Database Security
- Hash passwords (never plain text)
- Salt with bcrypt
- Parameterized queries
- Encrypted sensitive fields
â API Security
- HTTPS/TLS enforcement
- CORS properly configured
- Rate limiting
- Request size limits
â Frontend Security
- HTTP-Only cookies (no JS access)
- CSRF protection
- Secure headers (CSP, X-Frame-Options)
- Input validation
â Infrastructure
- Environment variables for secrets
- Secure key rotation
- Audit logging
- Monitoring and alerts
Resource Files Included
- FRAMEWORKS.md – Framework-specific implementations
- PATTERNS.md – All authentication patterns
- OAUTH.md – Social login & OAuth2
- MFA.md – Multi-factor authentication
- RBAC.md – Role & permission management
- SECURITY.md – Security best practices
- INTEGRATION.md – Database & email integration
- BETTER_AUTH.md – Better Auth guide
Scripts
- auth_generator.py – Generate auth boilerplate
- security_audit.py – Audit auth implementation