
security-patterns
Implements authentication, authorization, encryption, secrets management, and security hardening patterns. Use when designing auth flows, managing secrets, configuring CORS, implementing rate limiting, or when asked about JWT, OAuth, password hashing, API keys, RBAC, or security best practices.
- Installs
- 126
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/cloudai-x/claude-workflow-v2 --skill security-patterns- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads security-patterns automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Security Patterns
When to Load
- Trigger: Auth flows, encryption, secrets management, CORS configuration, input validation, rate limiting
- Skip: No security surface involved in the current task
Security Implementation Workflow
Copy this checklist and track progress:
Security Implementation Progress:
- [ ] Step 1: Choose authentication strategy
- [ ] Step 2: Implement authorization model
- [ ] Step 3: Set up password hashing
- [ ] Step 4: Configure secrets management
- [ ] Step 5: Enable encryption (transit + rest)
- [ ] Step 6: Configure CORS
- [ ] Step 7: Add rate limiting
- [ ] Step 8: Validate against anti-patterns checklist
Authentication Patterns
JWT (JSON Web Tokens)
import jwt from "jsonwebtoken";
function generateTokens(user: User) {
const accessToken = jwt.sign(
{ sub: user.id, role: user.role },
process.env.JWT_SECRET!,
{ expiresIn: "15m", algorithm: "HS256" },
);
const refreshToken = jwt.sign(
{ sub: user.id, tokenVersion: user.tokenVersion },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: "7d" },
);
return { accessToken, refreshToken };
}
// WRONG: localStorage (XSS vulnerable) | CORRECT: httpOnly cookie for refresh, memory for access
res.cookie("refreshToken", refreshToken, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 7 * 24 * 60 * 60 * 1000,
path: "/api/auth/refresh",
});
JWT Verification Middleware
function authenticate(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing token" });
}
try {
const token = header.slice(7);
const payload = jwt.verify(token, process.env.JWT_SECRET!, {
algorithms: ["HS256"], // pin: never let the token choose its algorithm
}) as JwtPayload;
req.user = { id: payload.sub, role: payload.role };
next();
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: "Token expired" });
}
return res.status(401).json({ error: "Invalid token" });
}
}
Session-Based Auth
import session from "express-session";
import { RedisStore } from "connect-redis";
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}),
);
OAuth 2.0 / OIDC Flow Summary
Authorization Code Flow with PKCE (all clients):
1. Redirect to provider: /authorize?response_type=code&client_id=...&redirect_uri=...&scope=openid email
&state=RANDOM&nonce=RANDOM&code_challenge=...&code_challenge_method=S256
2. User authenticates, provider redirects back with ?code=AUTHORIZATION_CODE&state=... (reject if state differs)
3. Backend exchanges code for tokens (POST /token with code_verifier, plus client_secret for confidential clients)
4. Backend validates the id_token (signature, iss, aud, exp, nonce), then creates session/JWT
Public clients (SPAs, mobile): same flow without client_secret
NEVER use Implicit Flow (deprecated, tokens exposed in URL)
API Key Authentication
async function authenticateApiKey(
req: Request,
res: Response,
next: NextFunction,
) {
const apiKey = req.headers["x-api-key"] as string;
if (!apiKey) return res.status(401).json({ error: "API key required" });
// WRONG: Direct comparison (timing attack) | CORRECT: Hash-based lookup
const hashedKey = crypto.createHash("sha256").update(apiKey).digest("hex");
const keyRecord = await db.apiKey.findUnique({ where: { hash: hashedKey } });
if (!keyRecord || keyRecord.revokedAt)
return res.status(401).json({ error: "Invalid API key" });
req.apiClient = { id: keyRecord.clientId, scopes: keyRecord.scopes };
next();
}
Authorization Models
RBAC (Role-Based Access Control)
const PERMISSIONS = {
admin: [
"users:read",
"users:write",
"users:delete",
"posts:read",
"posts:write",
"posts:delete",
],
editor: ["posts:read", "posts:write", "posts:delete", "users:read"],
viewer: ["posts:read", "users:read"],
} as const;
type Role = keyof typeof PERMISSIONS;
function authorize(...requiredPermissions: string[]) {
return (req: Request, res: Response, next: NextFunction) => {
const userPermissions = PERMISSIONS[req.user.role as Role] || [];
const hasPermission = requiredPermissions.every((p) =>
(userPermissions as readonly string[]).includes(p),
);
if (!hasPermission)
return res.status(403).json({ error: "Insufficient permissions" });
next();
};
}
// Usage: app.delete("/api/users/:id", authenticate, authorize("users:delete"), deleteUser);
Resource-Level Authorization
// WRONG: Only checking role, not ownership -- any editor can edit ANY post
// CORRECT: Check ownership or admin role
app.put(
"/api/posts/:id",
authenticate,
authorize("posts:write"),
async (req, res) => {
const post = await db.post.findUnique({ where: { id: req.params.id } });
if (!post) return res.status(404).json({ error: "Not found" });
if (post.authorId !== req.user.id && req.user.role !== "admin") {
return res
.status(403)
.json({ error: "Not authorized to edit this post" });
}
// WRONG: data: req.body -- mass assignment lets the client overwrite authorId
const { title, body } = UpdatePostSchema.parse(req.body); // allowlisted fields only
const updated = await db.post.update({
where: { id: post.id },
data: { title, body },
});
res.json(updated);
},
);
Password Handling
import bcrypt from "bcrypt";
// WRONG: plaintext or MD5/SHA256 (too fast, brute-forceable)
// CORRECT: bcrypt with appropriate cost factor
const SALT_ROUNDS = 12; // ~250ms on modern hardware
async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(
password: string,
hash: string,
): Promise<boolean> {
return bcrypt.compare(password, hash); // constant-time comparison built-in
}
// Registration
await db.user.create({
data: { email, password: await hashPassword(req.body.password) },
});
// Login -- WRONG: "Invalid password" (reveals email exists) | CORRECT: generic message
// Always run bcrypt, even for unknown emails, or response time reveals which emails exist
const user = await db.user.findUnique({ where: { email } });
const hash = user?.password ?? DUMMY_HASH; // bcrypt hash of a random string, created at startup
if (!(await verifyPassword(req.body.password, hash)) || !user) {
return res.status(401).json({ error: "Invalid email or password" });
}
Password Policies
function validatePassword(password: string): string[] {
const errors: string[] = [];
if (password.length < 12) errors.push("Minimum 12 characters");
if (Buffer.byteLength(password) > 72) errors.push("Maximum 72 bytes"); // bcrypt ignores the rest
// Check against breached password lists (haveibeenpwned API or local)
// Do NOT enforce arbitrary complexity rules (uppercase + number + symbol)
// NIST 800-63B recommends length over complexity
return errors;
}
Secrets Management
# WRONG: Hardcoded values in source code
# API_KEY = "some-value-here"
# CORRECT: Environment variables loaded from .env
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("API_KEY")
db_url = os.getenv("DATABASE_URL")
# CORRECT: Secrets manager for production
# AWS: Secrets Manager, Parameter Store
# GCP: Secret Manager
# HashiCorp Vault for self-hosted
Secret Rotation
1. Generate new secret value
2. Deploy code that accepts BOTH old and new values
3. Update all consumers to use the new value
4. Verify old value is no longer in use
5. Revoke old value
Never: Rotate in-place without a transition period
Encryption Patterns
In Transit
// Redirect HTTP to HTTPS in production
app.use((req, res, next) => {
if (
req.headers["x-forwarded-proto"] !== "https" &&
process.env.NODE_ENV === "production"
) {
return res.redirect(301, `https://${req.hostname}${req.url}`);
}
next();
});
// HSTS header
app.use((req, res, next) => {
res.setHeader(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
);
next();
});
At Rest
import crypto from "crypto";
const ALGORITHM = "aes-256-gcm";
function encrypt(
plaintext: string,
key: Buffer,
): { ciphertext: string; iv: string; tag: string } {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
let ciphertext =
cipher.update(plaintext, "utf8", "hex") + cipher.final("hex");
return {
ciphertext,
iv: iv.toString("hex"),
tag: cipher.getAuthTag().toString("hex"),
};
}
function decrypt(
ciphertext: string,
key: Buffer,
iv: string,
tag: string,
): string {
const decipher = crypto.createDecipheriv(
ALGORITHM,
key,
Buffer.from(iv, "hex"),
);
decipher.setAuthTag(Buffer.from(tag, "hex"));
return decipher.update(ciphertext, "hex", "utf8") + decipher.final("utf8");
}
// Use for PII, sensitive data. Encryption key in secrets manager, NOT in code.
CORS Configuration
import cors from "cors";
// WRONG: Allow everything
app.use(cors()); // origin: *, credentials: false
// WRONG: Wildcard with credentials
app.use(cors({ origin: "*", credentials: true })); // browsers reject this
// CORRECT: Explicit allowed origins
const ALLOWED_ORIGINS = [
"https://myapp.com",
"https://admin.myapp.com",
...(process.env.NODE_ENV !== "production" ? ["http://localhost:3000"] : []),
];
app.use(
cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
maxAge: 86400, // cache preflight for 24 hours
}),
);
Rate Limiting
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
import RedisStore from "rate-limit-redis";
// Global rate limit
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // 100 requests per window
standardHeaders: true, // RateLimit-* headers
legacyHeaders: false,
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
}),
);
// Strict limit on auth endpoints
app.use(
"/api/auth/login",
rateLimit({
windowMs: 15 * 60 * 1000,
limit: 5, // 5 login attempts per 15 min
message: { error: "Too many login attempts. Try again later." },
}),
);
// Per-API-key rate limiting for developer APIs
app.use(
"/api/v1/",
rateLimit({
windowMs: 60 * 1000, // 1 minute
limit: 60, // 60 requests per minute
keyGenerator: (req) => req.apiClient?.id ?? ipKeyGenerator(req.ip),
}),
);
Security Headers
import helmet from "helmet";
app.use(helmet()); // Sets many secure headers at once
// Key headers helmet sets:
// X-Content-Type-Options: nosniff
// X-Frame-Options: SAMEORIGIN
// Strict-Transport-Security: max-age=31536000; includeSubDomains
// Content-Security-Policy: default-src 'self'
// Customize CSP for your app
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://cdn.example.com"],
connectSrc: ["'self'", "https://api.example.com"],
},
}),
);
Input Validation
import { z } from "zod";
// WRONG: Trusting user input directly (SQL injection risk)
app.post("/api/users", (req, res) => {
db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);
});
// CORRECT: Validate with schema, use parameterized queries
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().trim().min(1).max(100),
age: z.number().int().min(13).max(150).optional(),
});
app.post("/api/users", async (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
// Use parameterized query (ORM or prepared statement)
await db.user.create({ data: result.data });
});
Common Anti-Patterns Summary
AVOID DO INSTEAD
-------------------------------------------------------------------
JWT in localStorage httpOnly secure cookie (refresh), memory (access)
MD5/SHA for passwords bcrypt or argon2 with proper cost factor
Hardcoded secrets in code Environment variables + secrets manager
cors({ origin: '*' }) Explicit allowed origins list
"Invalid password" message "Invalid email or password" (no enumeration)
No rate limiting on auth Strict rate limits on login/register
Rolling your own crypto Use established libraries (jose, bcrypt)
Trusting user input Validate with zod/joi, parameterized queries
Same API key forever Rotate keys regularly, support multiple active
No HTTPS redirect Force HTTPS + HSTS header
Symmetric JWT for multi-service Use RS256/ES256 (asymmetric) for distributed
No input length limits Max length on all string inputs
FAQ
What does the security-patterns skill do?
Implements authentication, authorization, encryption, secrets management, and security hardening patterns. Use when designing auth flows, managing secrets, configuring CORS, implementing rate limiting, or when asked about JWT, OAuth, password hashing, API keys, RBAC, or security best practices.
How do I install security-patterns?
Run npx skills add https://github.com/cloudai-x/claude-workflow-v2 --skill security-patterns in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/security-patterns/ (personal) or .claude/skills/security-patterns/ (project).
Which AI agents can use security-patterns?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is security-patterns safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains security-patterns?
It is published by cloudai-x in cloudai-x/claude-workflow-v2. It has 126 recorded installs.
More from cloudai-x/claude-workflow-v2
analyzing-projects
cloudai-x/claude-workflow-v2
Open the skill to read its SKILL.md instructions.
designing-architecture
cloudai-x/claude-workflow-v2
Open the skill to read its SKILL.md instructions.
convex-backend
cloudai-x/claude-workflow-v2
Open the skill to read its SKILL.md instructions.
parallel-execution
cloudai-x/claude-workflow-v2
Patterns for parallel subagent execution using the Agent tool (formerly Task). Use when coordinating multiple independent tasks, spawning dynamic subagents, or implementing features that can be parallelized.
optimizing-performance
cloudai-x/claude-workflow-v2
Open the skill to read its SKILL.md instructions.
managing-git
cloudai-x/claude-workflow-v2
Open the skill to read its SKILL.md instructions.
Popular security skills
azure-compliance
microsoft/azure-skills
Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.
minimal-run-and-audit
lllllllama/rigorpilot-skills
Rigor Run skill for README-first deep learning repo reproduction. Use when the task is specifically to capture or normalize evidence from the selected smoke test or documented inference or evaluation command and write standardized reprooutputs/ files, including patch notes when repository files changed. Do not use for training execution, initial repo intake, generic environment setup, paper lookup, target selection, hidden scientific-meaning changes, or end-to-end orchestration by itself.
seo-audit
coreyhaines31/marketingskills
When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," "indexing issues," "stuck on page 2," "striking distance keywords," "title tag rewrite," "low CTR," "local SEO," "Google Business Profile," or "map pack." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema. For AI search optimization, see ai-seo.
firebase-security-rules-auditor
firebase/agent-skills
Audits Firebase (Firestore, Cloud Storage) security rules for vulnerabilities, privilege escalation, role bypasses, create vs update inconsistencies, resource exhaustion, type safety, size limits, and hasOnly ownership checks. Use when auditing/reviewing rules, running red-team rule assessments, or scoring against auditor checklists. Don't use for Firebase CLI (login, deploy), Auth, Crashlytics, Remote Config, or database queries.
audit
pbakaus/impeccable
Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
competitors
coreyhaines31/marketingskills
When the user wants to create competitor comparison or alternative pages for SEO and buyer-facing use. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'competitor teardown,' 'audit our competitor pages,' 'are our comparison pages out of date,' or 'competitive asset audit.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For auditing existing claims (not researching competitors from scratch, which is competitor-profiling; not technical SEO on these pages, which is seo-audit), use the asset audit here. For internal battle cards and sales-specific competitor docs, see sales-enablement.