算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
backend-auth-patterns【设计与创意】Use this skill when the user says 'auth', 'authentication', 'authorization', 'JWT', 'OAuth', 'RBAC', 'permissions', 'login', 'session', 'refresh token', 'guard', 'middleware auth', or when implementing or reviewing authentication and authorization. This skill enforces: JWT structure and validation, refresh token rotation, OAuth2/OIDC flows, RBAC vs A…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Implement authentication and authorization that is secure by default. Every endpoint must have an auth check. No secrets in code. No plaintext passwords.
Exact user phrases: "auth", "authentication", "authorization", "JWT", "OAuth", "RBAC", "permissions", "login", "session", "refresh token", "guard", "middleware auth", "access control", "secure endpoint".
Auth design as text. No file output.
Auth strategy: {strategy name}
Rationale: {one sentence}
Token structure: {claims}
Auth middleware: {location and logic}
Authorization model: {model name and role/permission list}
Browser-based SPA?
├── Yes → JWT with refresh tokens in httpOnly cookies (prevents XSS token theft)
│ └── CSRF protection required (SameSite=Strict + CSRF token)
└── No → Mobile app?
├── Yes → JWT with refresh tokens in secure device storage (Keychain/Keystore)
│ └── No CSRF needed (native apps can set custom headers)
└── No → Server-side rendered app?
├── Yes → Session-based auth with Redis store (simpler, CSRF protection built-in)
└── No → M2M service?
├── Yes → API Key (simple) or OAuth2 Client Credentials (standard)
└── No → Third-party login?
└── OAuth2 / OIDC delegated to provider
Single service / monolith?
├── Yes → HMAC with HS256 (simpler, faster, one secret to manage)
└── No → Multiple services?
├── Yes → RSA with RS256 or EC with ES256 (public key distribution, no shared secret)
└── Cloud-native with JWKS endpoint?
└── RS256 with JWKS rotation (standard for OIDC)
Do you need to revoke sessions immediately?
├── Yes → Session-based (server-side state, immediate revocation)
└── No → Is horizontal scaling a priority?
├── Yes → JWT (stateless, no shared session store)
└── No → Session-based (simpler, built-in revocation)
Is CSRF protection a concern?
├── Yes → Session with SameSite=Strict cookie + CSRF token
└── No → JWT with Bearer header (CSRF inherently protected)
| Application Type | Strategy | Token Type | Storage |
|---|---|---|---|
| SPA (browser) | JWT + Refresh Token | Access in memory, Refresh in httpOnly cookie | Memory + Cookie |
| Mobile app | JWT + Refresh Token | Both in secure storage | Keychain/Keystore |
| M2M service | API Key or Client Credentials | Static key or short-lived JWT | Env/Secrets |
| Server-rendered app | Session + Cookie | Session ID in signed cookie | Redis store |
| Third-party login | OAuth2 / OIDC | Delegated to provider | Provider-managed |
| Enterprise SSO | OIDC with SAML | Delegated to IdP | IdP-managed |
Token claims (minimum):
{
"sub": "user-uuid",
"role": "admin",
"iat": 1700000000,
"exp": 1700086400
}
Common extended claims:
{
"sub": "user-uuid",
"iss": "https://api.example.com",
"aud": "web-app",
"role": "admin",
"permissions": ["order:read", "order:write"],
"iat": 1700000000,
"exp": 1700086400,
"jti": "unique-token-id"
}
Rules:
1. Client sends access_token + refresh_token
2. Server validates signature and expiry of both
3. If access_token expired but refresh_token valid:
a. Issue new access_token (15 min)
b. Rotate refresh_token (issue new, invalidate old)
c. Return both
4. If a consumed refresh_token is reused:
a. Invalidate ALL refresh tokens for that user
b. Log security event
c. Require re-authentication
Implementation:
interface TokenPair {
accessToken: string;
refreshToken: string;
}
async function refreshTokens(refreshToken: string): Promise<TokenPair> {
const stored = await tokenRepository.findByToken(refreshToken);
if (!stored || stored.consumedAt) {
// Token reuse detected
await tokenRepository.consumeAllForUser(stored?.userId);
await securityLogger.log('refresh_token_reuse', { token: refreshToken });
throw new UnauthorizedError('Token reuse detected. All sessions invalidated.');
}
if (stored.expiresAt < new Date()) {
throw new UnauthorizedError('Refresh token expired.');
}
await tokenRepository.markConsumed(refreshToken);
const user = await userRepository.findById(stored.userId);
const accessToken = generateAccessToken(user);
const newRefreshToken = generateRefreshToken(user);
await tokenRepository.save({
token: newRefreshToken,
userId: user.id,
expiresAt: addDays(new Date(), 7),
});
return { accessToken, refreshToken: newRefreshToken };
}
RBAC (Role-Based Access Control) — default for most applications:
const ROLES = {
admin: ['*'],
manager: ['order:read', 'order:write', 'user:read'],
user: ['order:read', 'order:write'],
guest: ['order:read'],
} as const;
function authorize(requiredPermission: string) {
return (req: Request, res: Response, next: NextFunction) => {
const user = req.user;
const permissions = ROLES[user.role] || [];
if (!permissions.includes('*') && !permissions.includes(requiredPermission)) {
throw new ForbiddenError('Insufficient permissions');
}
next();
};
}
ABAC (Attribute-Based Access Control) — for fine-grained control:
POLICIES = [
{
"effect": "allow",
"action": "order:read",
"condition": lambda user, resource, env:
user.tenant_id == resource.tenant_id
},
{
"effect": "allow",
"action": "order:delete",
"condition": lambda user, resource, env:
user.role == "admin" or resource.created_by == user.id
}
]
def check_abac(user, action, resource):
for policy in POLICIES:
if policy["action"] == action and policy["condition"](user, resource, None):
return True
return False
Auth middleware belongs in Infrastructure layer. Not in Domain. Not in Application.
// Express middleware
import jwt from 'jsonwebtoken';
function authenticate(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
throw new UnauthorizedError('Missing or invalid authorization header');
}
const token = authHeader.substring(7);
try {
const payload = jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
algorithms: ['RS256'],
issuer: process.env.JWT_ISSUER,
});
req.user = payload;
next();
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
throw new UnauthorizedError('Token expired');
}
throw new UnauthorizedError('Invalid token');
}
}
Python FastAPI middleware:
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
security = HTTPBearer()
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
try:
payload = jwt.decode(
token,
PUBLIC_KEY,
algorithms=["RS256"],
issuer="https://api.example.com",
audience="web-app",
)
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
import { hash, compare } from 'bcrypt';
const SALT_ROUNDS = 12;
export async function hashPassword(plain: string): Promise<string> {
return hash(plain, SALT_ROUNDS);
}
export async function verifyPassword(plain: string, hashed: string): Promise<boolean> {
return compare(plain, hashed);
}
// Argon2 alternative (preferred for new projects)
import * as argon2 from 'argon2';
export async function hashPasswordArgon2(plain: string): Promise<string> {
return argon2.hash(plain, {
type: argon2.argon2id,
memoryCost: 19456,
timeCost: 2,
parallelism: 1,
});
}
const AUTH_RATE_LIMITS = {
login: { window: '5 minutes', max: 5 },
register: { window: '60 minutes', max: 2 },
refresh: { window: '1 minute', max: 10 },
passwordReset: { window: '60 minutes', max: 3 },
};
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 5 * 60 * 1000,
max: 5,
standardHeaders: true,
legacyHeaders: false,
message: { code: 'RATE_LIMITED', message: 'Too many login attempts. Try again later.' },
keyGenerator: (req) => `${req.ip}-${req.body?.email}`, // Per-IP + per-user
});
app.use('/api/auth/login', loginLimiter);
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
name: 'sid',
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
resave: false,
saveUninitialized: false,
rolling: true, // Reset maxAge on each request
}));
// Regenerate session ID on login (prevents session fixation)
req.session.regenerate((err) => {
req.session.userId = user.id;
req.session.role = user.role;
});
For immediate revocation of JWTs, maintain a token blacklist:
// Store in Redis with TTL matching token expiry
async function revokeToken(jti: string, exp: number): Promise<void> {
const ttl = exp - Math.floor(Date.now() / 1000);
await redis.set(`revoked:${jti}`, 'true', 'EX', Math.max(ttl, 60));
}
async function isTokenRevoked(jti: string): Promise<boolean> {
return (await redis.exists(`revoked:${jti}`)) === 1;
}
| Flow | Use Case | Security |
|---|---|---|
| Authorization Code | Web apps with backend | Best — PKCE required |
| Authorization Code + PKCE | SPA, mobile | Required for native apps |
| Client Credentials | M2M service | No user context |
| Resource Owner Password | Legacy / trusted apps | Avoid — exposes credentials |
| Implicit | Legacy SPAs | Deprecated — use PKCE |
exp server-side.Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Cache-Control: no-store (never cache auth responses)
| Feature | JWT + Refresh | Session Cookies | OAuth2 / OIDC |
|---|---|---|---|
| Stateful/Stateless | Stateless (access), Stateful (refresh) | Stateful | Stateless |
| Scalability | Excellent | Requires shared session store | Excellent |
| XSS protection | Requires httpOnly cookie | httpOnly cookie | Delegated |
| CSRF protection | Via configuration | Via tokens | Via state param |
| Mobile support | Native | Requires webview | Native SDK |
| Token revocation | Blacklist required | Immediate | Per-provider |
| Implementation complexity | Moderate | Low | High |
| Tool | Purpose |
|---|---|
| jsonwebtoken / jose | JWT creation and verification |
| bcrypt / argon2 | Password hashing |
| express-rate-limit | Rate limiting middleware |
| helmet | HTTP security headers |
| connect-redis | Redis session store |
| passport.js | Strategy-based auth middleware |
| keycloak-connect | Keycloak integration |
| auth0 | Auth0 integration SDK |
No artifact produced. Next skill: backend-testing — test auth flows, verify auth middleware, test rate limiting. Carry forward: auth strategy, token structure, authorization model, middleware implementation details.
` interface EventObserver { onEvent(event: T): Promise; }
class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% over 5min | Critical | Page on-call |
| p99 latency | > 2s over 5min | Warning | Investigate |
| Throughput drop | > 50% over 1min | Critical | Check upstream |
| Queue depth | > 1000 over 1min | Warning | Scale consumers |
| Disk usage | > 85% | Warning | Clean or expand |
| Memory usage | > 90% heap | Critical | Restart or scale |
| Anti-Pattern | Symptom | Root Cause | Solution |
|---|---|---|---|
| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |
| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |
| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |
| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).