Secure Code Guardian
Implements authentication, authorization, and input validation with concrete secure defaults, preventing OWASP Top 10 vulnerabilities in new code.
Before & after
Same prompt, same agent — the only difference is the skill.
I can help you think through JWT authentication. You'd need to handle token generation, validation, and refresh logic. You'll want to consider where to store the secret key, how long tokens should be valid, and how to handle token expiration. You'd also want to make sure you're hashing passwords properly if you're building user registration. There are a lot of best practices around JWT—using strong algorithms, securing the keys, handling edge cases. If you can tell me what framework you're using, I can give you more specific guidance on implementation approaches.
JWT AUTHENTICATION IMPLEMENTATION
FILE STRUCTURE:
/app/api/auth/
login.ts (POST: email + password → JWT token)
token-validate.ts (POST: validate token + return claims)
refresh.ts (POST: refresh_token → new JWT)
logout.ts (POST: invalidate token)
STEP 1: PASSWORD HASHING (Before token generation)
Library: bcrypt (or argon2 for higher security)
Code (Node.js):
const bcrypt = require("bcrypt");
const SALT_ROUNDS = 12;
// On signup:
const passwordHash = await bcrypt.hash(userPassword, SALT_ROUNDS);
await db.users.create({ email, passwordHash });
// On login:
const user = await db.users.findByEmail(email);
const isValid = await bcrypt.compare(userPassword, user.passwordHash);
if (!isValid) throw new Error("Invalid credentials");
Rationale: bcrypt with 12 rounds = ~250ms per hash (slows down brute force attacks). Never store plaintext passwords.
STEP 2: JWT TOKEN GENERATION
Library: jsonwebtoken (Node.js)
Secret key storage: Use environment variable (JWT_SECRET in .env.local)
Minimum secret length: 32 bytes (256 bits)
Code:
const jwt = require("jsonwebtoken");
const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRY = "15m"; // Short-lived access token
// Generate access token (short-lived)
const accessToken = jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role, // for authorization checks
},
JWT_SECRET,
{ expiresIn: JWT_EXPIRY, algorithm: "HS256" }
);
// Generate refresh token (long-lived, stored in DB)
const refreshToken = jwt.sign(
{ userId: user.id, tokenVersion: user.tokenVersion },
JWT_SECRET,
{ expiresIn: "7d" }
);
// Store refresh token in database (tied to user)
await db.refreshTokens.create({ userId: user.id, token: refreshToken });
Return to client:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 900 // seconds (15 min)
}
STEP 3: TOKEN VALIDATION MIDDLEWARE
Code (attach to protected routes):
const validateJWT = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing token" });
}
const token = authHeader.slice(7);
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded; // Attach user claims to request
next();
} catch (err) {
if (err.name === "TokenExpiredError") {
return res.status(401).json({ error: "Token expired. Use refresh endpoint." });
}
return res.status(403).json({ error: "Invalid token" });
}
};
Usage on protected endpoints:
app.post("/api/user-data", validateJWT, (req, res) => {
// req.user.userId is now available
res.json({ userId: req.user.userId, data: "your data" });
});
STEP 4: TOKEN REFRESH LOGIC
Code:
app.post("/api/auth/refresh", async (req, res) => {
const { refreshToken } = req.body;
// Validate refresh token signature
try {
const decoded = jwt.verify(refreshToken, JWT_SECRET);
} catch (err) {
return res.status(401).json({ error: "Refresh token invalid" });
}
// Check if refresh token exists in database (prevents token reuse after logout)
const tokenRecord = await db.refreshTokens.findOne({ token: refreshToken });
if (!tokenRecord) {
return res.status(401).json({ error: "Token revoked" });
}
// Verify token version (prevents old refresh tokens after password reset)
const user = await db.users.findById(decoded.userId);
if (user.tokenVersion !== decoded.tokenVersion) {
return res.status(401).json({ error: "Token version mismatch" });
}
// Generate new access token
const newAccessToken = jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
JWT_SECRET,
{ expiresIn: "15m" }
);
res.json({ accessToken: newAccessToken, expiresIn: 900 });
});
Rationale: Refresh tokens are stored in DB so they can be revoked. Access tokens are short-lived (15 min) to limit exposure if stolen.
STEP 5: LOGOUT (REVOKE REFRESH TOKEN)
Code:
app.post("/api/auth/logout", validateJWT, async (req, res) => {
const { refreshToken } = req.body;
await db.refreshTokens.deleteWhere({ token: refreshToken });
res.json({ message: "Logged out" });
});
Rationale: Delete the refresh token from database so it can't be reused.
STEP 6: SECURITY HARDENING
SECURE TRANSPORT:
• Always send JWT over HTTPS (never HTTP)
• In API Gateway: Enforce HTTPS, set Strict-Transport-Security header
STORAGE ON CLIENT:
• Option A: Store in secure httpOnly cookie (immune to XSS, but vulnerable to CSRF)
res.cookie("accessToken", token, { httpOnly: true, secure: true, sameSite: "Strict" });
• Option B: Store in localStorage (vulnerable to XSS, immune to CSRF)
• Recommendation: Use httpOnly cookie + CSRF token
KEY ROTATION:
• Rotate JWT_SECRET every 90 days
• Keep old secret for 7 days (allows in-flight tokens to validate)
• Invalidate all refresh tokens when rotating (force re-login)
ALGORITHM CHOICE:
• Use HS256 (HMAC-SHA256) for symmetric signing (single secret)
• Use RS256 (RSA) if you need asymmetric signing (public key verification from other services)
• Never use HS256 + public verification (signature is trivial to forge)
CLAIM VALIDATION:
• Always validate token signature (jwt.verify does this)
• Always check expiration claim (jwt.verify does this)
• Always verify the algorithm matches (jwt.verify does this by default)
EXAMPLE TEST CASE:
Describe("JWT Auth", () => {
it("should reject expired token", () => {
const expiredToken = jwt.sign({ userId: 1 }, JWT_SECRET, { expiresIn: "-1s" });
const res = validateJWT({ headers: { authorization: `Bearer ${expiredToken}` } });
expect(res.status).toBe(401);
});
it("should accept valid token", () => {
const token = jwt.sign({ userId: 1 }, JWT_SECRET, { expiresIn: "15m" });
const res = validateJWT({ headers: { authorization: `Bearer ${token}` } });
expect(res.user.userId).toBe(1);
});
});
DEPLOYMENT CHECKLIST:
✓ JWT_SECRET set in production .env (32+ bytes, random)
✓ HTTPS enforced on all endpoints
✓ Refresh tokens stored in database (not in JWT)
✓ Token expiration validated on each request
✓ Password hashing with bcrypt/argon2 (not plain text)
✓ Unit tests for token refresh, expiration, revocation
✓ Error messages don't leak user existence ("Invalid credentials" not "User not found")
✓ Rate limiting on /auth endpoints (prevent brute force: max 5 login attempts per minute per IP)
Note: This is a security implementation reference, not comprehensive security advice. Have your security team review before production.About this skill
name: secure-code-guardian description: Use when Use when implementing authentication/authorization, securing user input, or preventing OWASP Top 10 vulnerabilities — including custom security implementations such as hashing passwords with bcrypt...
Secure Code Guardian
Use when implementing authentication/authorization, securing user input, or preventing OWASP Top 10 vulnerabilities — including custom security implementations such as hashing passwords with bcrypt/argon2, sanitizing SQL queries with parameterized statements, configuring CORS/CSP headers, validating input with Zod, and setting up JWT tokens. Invoke for authentication, authorization, input validation, encryption, OWASP Top 10 prevention, secure session management, and security hardening. For pre-built OAuth/SSO integrations or standalone security audits, consider a more specialized skill.
What you get
- Public GitHub repo
- the skills/secure-code-guardian folder with SKILL.md and references.
Customize your output
- Fork the repo and extend the skill's reference files for your own stack conventions.
Example output
Activates on a matching request (e.g. building or reviewing Secure Code Guardian code) and can chain with other skills in the pack.
Best for
Full-stack developers and engineering teams using Claude Code.
SKILL.md preview
---
name: secure-code-guardian
description: Use this skill when implementing authentication, authorization, or input validation to prevent OWASP Top 10 vulnerabilities.
version: 1.0.0
category: Development / Security
author: AgentVolt
license: proprietary
tags:
- development
- security
---
# Secure Code Guardian
Implements authentication, authorization, and input validation with concrete secure defaults, preventing OWASP Top 10 vulnerabilities in new code.
## When to use
… (sign up to view the full skill)More development skills
View all Development skills →Security Scanning
Scans code changes for common security issues before they ship.
Security Reviewer
Identifies security vulnerabilities, generates structured audit reports with severity ratings, and provides actionable remediation guidance.
Fullstack Guardian
Builds security-focused full-stack web applications by implementing integrated frontend and backend components with layered security at every level.
Trail of Bits Security Skill
Audits your code for security issues before you ship, using Trail of Bits' professional security review methodology.