Secure Code Guardian

Implements authentication, authorization, and input validation with concrete secure defaults, preventing OWASP Top 10 vulnerabilities in new code.

Development / Securitydevelopmentsecurity
by AgentVoltv1.0.0Published 1y ago1 install
Free to sign up · every skill included with AgentVolt Pro

Before & after

Same prompt, same agent — the only difference is the skill.

PromptImplement JWT token authentication for our API
Beforegeneric AI agent
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.
Afterwith Secure Code Guardian
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

SKILL.md
---
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)
Sign up to view, copy, and install the full skill

More development skills

View all Development skills →