
Secure Code Guardian
Drop in vetted bcrypt, JWT, and password-policy patterns when you are implementing auth in a TypeScript backend.
Overview
secure-code-guardian is an agent skill most often used in Ship (also Build backend) that supplies TypeScript patterns for bcrypt passwords, JWT access/refresh tokens, and strict password validation.
Install
npx skills add https://github.com/jeffallan/claude-skills --skill secure-code-guardianWhat is this skill?
- bcrypt password hashing with 12 salt rounds and compare helpers
- JWT access (15m) and refresh (7d) token generation with typed payloads
- Password validator enforcing minimum 12 characters plus character-class rules
- TypeScript-first snippets suitable for copy into API route handlers
- Environment-backed JWT secret pattern (`process.env.JWT_SECRET`)
- bcrypt SALT_ROUNDS = 12
- Access token expiry 15m and refresh token expiry 7d
- Password policy minimum 12 characters with upper, lower, digit, and special character requirements
Adoption & trust: 2.8k installs on skills.sh; 9.7k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are coding login flows yourself and risk weak hashing, sloppy JWT expiry, or passwords that do not meet baseline policy.
Who is it for?
Solo builders shipping a TypeScript/Node SaaS or API who want opinionated secure-auth snippets during implementation or pre-release review.
Skip if: Non-TypeScript stacks, teams needing full IAM/OAuth provider integration only, or products that should use passwordless-only auth without local credential storage.
When should I use this skill?
Implementing or reviewing password hashing, JWT issuance, or password validation in a TypeScript backend.
What do I get? / Deliverables
You paste auditable TypeScript auth primitives with defined salt rounds, token lifetimes, and validation rules ready for integration into your API.
- bcrypt hash and verify functions
- JWT access/refresh generators with typed payloads
- Password validation helper with structured errors
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Ship → security because the skill encodes release-ready authentication controls, though you often apply the snippets while building the backend. Security subphase matches credential hashing, token lifetimes, and validation rules—not generic CRUD scaffolding.
Where it fits
Scaffold register and login routes using the documented bcrypt and JWT helpers instead of one-off crypto code.
Audit existing auth handlers against the skill’s 12-round hashing and 15m/7d token split before go-live.
How it compares
Secure auth code templates—not a dynamic SAST scanner or a secrets vault integration.
Common Questions / FAQ
Who is secure-code-guardian for?
Indie developers building TypeScript backends who want agent-guided bcrypt and JWT implementations aligned to common production defaults.
When should I use secure-code-guardian?
During Build backend while writing auth modules, and again in Ship security before launch—to standardize hashing, JWT expiry, and password rules.
Is secure-code-guardian safe to install?
It is instructional code patterns only; review the Security Audits panel on this Prism page and never commit real JWT secrets—load them from environment variables.
SKILL.md
READMESKILL.md - Secure Code Guardian
# Authentication ## Password Hashing ```typescript import bcrypt from 'bcrypt'; const SALT_ROUNDS = 12; 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); } // Password requirements const PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{12,}$/; function validatePassword(password: string): { valid: boolean; errors: string[] } { const errors: string[] = []; if (password.length < 12) errors.push('Minimum 12 characters'); if (!/[a-z]/.test(password)) errors.push('Requires lowercase'); if (!/[A-Z]/.test(password)) errors.push('Requires uppercase'); if (!/\d/.test(password)) errors.push('Requires digit'); if (!/[@$!%*?&]/.test(password)) errors.push('Requires special character'); return { valid: errors.length === 0, errors }; } ``` ## JWT Implementation ```typescript import jwt from 'jsonwebtoken'; const JWT_SECRET = process.env.JWT_SECRET!; const ACCESS_TOKEN_EXPIRY = '15m'; const REFRESH_TOKEN_EXPIRY = '7d'; interface TokenPayload { sub: string; type: 'access' | 'refresh'; } function generateAccessToken(userId: string): string { return jwt.sign( { sub: userId, type: 'access' }, JWT_SECRET, { expiresIn: ACCESS_TOKEN_EXPIRY } ); } function generateRefreshToken(userId: string): string { return jwt.sign( { sub: userId, type: 'refresh' }, JWT_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRY } ); } function verifyToken(token: string): TokenPayload { return jwt.verify(token, JWT_SECRET) as TokenPayload; } ``` ## Auth Middleware ```typescript function authMiddleware(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 = verifyToken(token); if (payload.type !== 'access') { return res.status(401).json({ error: 'Invalid token type' }); } req.userId = payload.sub; next(); } catch (error) { if (error instanceof jwt.TokenExpiredError) { return res.status(401).json({ error: 'Token expired' }); } return res.status(401).json({ error: 'Invalid token' }); } } ``` ## Account Lockout ```typescript const MAX_ATTEMPTS = 5; const LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes async function handleLoginAttempt(email: string, success: boolean) { const key = `login:attempts:${email}`; if (success) { await redis.del(key); return; } const attempts = await redis.incr(key); await redis.expire(key, LOCKOUT_DURATION / 1000); if (attempts >= MAX_ATTEMPTS) { await redis.set(`login:locked:${email}`, '1', 'PX', LOCKOUT_DURATION); throw new Error('Account locked. Try again later.'); } } ``` ## Quick Reference | Practice | Implementation | |----------|----------------| | Password hash | bcrypt (12+ rounds) | | Token expiry | Access: 15m, Refresh: 7d | | Lockout | 5 attempts, 15min lockout | | MFA | TOTP (authenticator apps) | | JWT Claim | Purpose | |-----------|---------| | `sub` | User ID | | `exp` | Expiration | | `iat` | Issued at | | `type` | access/refresh | # Input Validation ## Zod Validation ```typescript import { z } from 'zod'; const UserSchema = z.object({ email: z.string().email().max(255), name: z.string().min(1).max(100).regex(/^[\w\s-]+$/), age: z.number().int().min(0).max(150).optional(), role: z.enum(['user', 'admin']).default('user'), }); function validateUser(data: unknown) { return UserSchema.parse(data); // Throws on invalid } // Safe parse (no throw) const result = UserSchema.safeParse(data); if (!result.success) { console.error(result.error.issues); } ``` ## SQL Injection Prevention ```typescript // ❌ NEVER do this const bad = `SELECT * FROM users WHERE id =