supabase
Handles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
jwt-security
Guidelines for implementing JWT authentication with security best practices for token creation, validation, and storage
Full skill instructions
You are an expert in JSON Web Token (JWT) security implementation. Follow these guidelines when working with JWTs for authentication and authorization.
A JWT consists of three parts: Header, Payload, and Signature.
header.payload.signature
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-identifier-for-rotation"
}
kid (key ID) for key rotation supporttyp: "JWT" explicitlyalg: "none"{
"iss": "https://auth.example.com",
"sub": "user-uuid-here",
"aud": "https://api.example.com",
"exp": 1704067200,
"iat": 1704063600,
"nbf": 1704063600,
"jti": "unique-token-id"
}
Required claims:
iss (issuer): Who created the tokensub (subject): Who the token representsaud (audience): Who the token is intended forexp (expiration): When the token expiresiat (issued at): When the token was createdRecommended claims:
nbf (not before): Token not valid before this timejti (JWT ID): Unique identifier for token revocation// RS256 - RSA with SHA-256 (most widely supported)
// ES256 - ECDSA with P-256 and SHA-256 (smaller keys)
// EdDSA - Edwards-curve Digital Signature Algorithm (most secure)
const ALLOWED_ALGORITHMS = ['RS256', 'ES256', 'EdDSA'];
// HS256 - HMAC with SHA-256
// Only use with a strong secret (minimum 256 bits / 32 bytes)
const secret = crypto.randomBytes(64).toString('hex');
const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('private.pem');
function createToken(userId, roles) {
const payload = {
sub: userId,
roles: roles,
// Keep custom claims minimal
};
const options = {
algorithm: 'RS256',
expiresIn: '15m', // Short-lived access tokens
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
keyid: 'current-key-id',
};
return jwt.sign(payload, privateKey, options);
}
const TOKEN_LIFETIMES = {
accessToken: '15m', // 15 minutes max
refreshToken: '7d', // 7 days with rotation
idToken: '1h', // 1 hour
passwordReset: '15m', // 15 minutes
emailVerification: '24h', // 24 hours
};
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
// JWKS client for fetching public keys
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 600000, // 10 minutes
rateLimit: true,
jwksRequestsPerMinute: 10,
});
async function validateToken(token) {
// 1. Decode header without verification to get kid
const decoded = jwt.decode(token, { complete: true });
if (!decoded) {
throw new Error('Invalid token format');
}
// 2. Validate algorithm against whitelist
if (!ALLOWED_ALGORITHMS.includes(decoded.header.alg)) {
throw new Error(`Algorithm ${decoded.header.alg} not allowed`);
}
// 3. Get signing key
const key = await client.getSigningKey(decoded.header.kid);
const publicKey = key.getPublicKey();
// 4. Verify signature and claims
const verified = jwt.verify(token, publicKey, {
algorithms: ALLOWED_ALGORITHMS, // Whitelist algorithms
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
clockTolerance: 30, // 30 seconds clock skew tolerance
});
return verified;
}
function validateTokenClaims(decoded) {
const now = Math.floor(Date.now() / 1000);
// 1. Check expiration
if (decoded.exp && decoded.exp < now) {
throw new Error('Token expired');
}
// 2. Check not before
if (decoded.nbf && decoded.nbf > now) {
throw new Error('Token not yet valid');
}
// 3. Check issuer
if (decoded.iss !== EXPECTED_ISSUER) {
throw new Error('Invalid issuer');
}
// 4. Check audience
const audiences = Array.isArray(decoded.aud) ? decoded.aud : [decoded.aud];
if (!audiences.includes(EXPECTED_AUDIENCE)) {
throw new Error('Invalid audience');
}
// 5. Check required claims exist
if (!decoded.sub) {
throw new Error('Missing subject claim');
}
return true;
}
// WRONG: Accepting any algorithm
jwt.verify(token, secret); // Vulnerable!
// CORRECT: Whitelist allowed algorithms
jwt.verify(token, key, { algorithms: ['RS256'] });
// Always reject 'none' algorithm
if (decoded.header.alg === 'none' || decoded.header.alg.toLowerCase() === 'none') {
throw new Error('Algorithm none is not allowed');
}
// When using asymmetric keys, never allow symmetric algorithms
const ASYMMETRIC_ONLY = ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'EdDSA'];
jwt.verify(token, publicKey, { algorithms: ASYMMETRIC_ONLY });
// Minimum 256-bit (32 byte) secret for HS256
// Minimum 384-bit (48 byte) secret for HS384
// Minimum 512-bit (64 byte) secret for HS512
function generateHmacSecret(algorithm) {
const bits = parseInt(algorithm.slice(2)); // HS256 -> 256
const bytes = bits / 8;
return crypto.randomBytes(Math.max(bytes, 32)).toString('hex');
}
// Best: HttpOnly cookie (requires backend support)
// Server sets:
res.cookie('access_token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 900000, // 15 minutes
});
// Acceptable: In-memory (lost on refresh)
let accessToken = null;
function setToken(token) {
accessToken = token;
}
// Avoid: localStorage (vulnerable to XSS)
// Avoid: sessionStorage for sensitive tokens
// Always use Authorization header
fetch('/api/resource', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Never put tokens in URLs (logged, cached, visible in history)
// WRONG: /api/resource?token=eyJ...
// Refresh tokens should be:
// 1. Stored securely (httpOnly cookie or secure server-side storage)
// 2. Rotated on each use
// 3. Bound to the client (if possible)
async function refreshAccessToken(refreshToken) {
// Validate refresh token
const decoded = await validateRefreshToken(refreshToken);
// Check if token has been revoked
const isRevoked = await checkTokenRevocation(decoded.jti);
if (isRevoked) {
throw new Error('Refresh token has been revoked');
}
// Generate new tokens
const newAccessToken = createAccessToken(decoded.sub);
const newRefreshToken = createRefreshToken(decoded.sub);
// Revoke old refresh token (rotation)
await revokeToken(decoded.jti);
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
}
// Maintain a revocation list for early token invalidation
const revokedTokens = new Set(); // Use Redis in production
function revokeToken(jti) {
revokedTokens.add(jti);
}
function isTokenRevoked(jti) {
return revokedTokens.has(jti);
}
// Include revocation check in validation
async function validateToken(token) {
const decoded = jwt.verify(token, key, options);
if (decoded.jti && isTokenRevoked(decoded.jti)) {
throw new Error('Token has been revoked');
}
return decoded;
}
// Support multiple keys during rotation
const keyStore = {
'key-2024-01': { /* current key */ },
'key-2023-12': { /* previous key, still valid */ },
};
// JWKS endpoint should expose all valid public keys
app.get('/.well-known/jwks.json', (req, res) => {
const keys = Object.entries(keyStore).map(([kid, key]) => ({
kid,
kty: 'RSA',
use: 'sig',
alg: 'RS256',
n: key.publicKey.n,
e: key.publicKey.e,
}));
res.json({ keys });
});
const expressJwt = require('express-jwt');
const jwksRsa = require('jwks-rsa');
const jwtMiddleware = expressJwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
}),
audience: 'https://api.example.com',
issuer: 'https://auth.example.com',
algorithms: ['RS256'],
});
// Protected route
app.get('/api/protected', jwtMiddleware, (req, res) => {
// req.auth contains the decoded token
res.json({ user: req.auth.sub });
});
describe('JWT Validation', () => {
it('should reject expired tokens', async () => {
const expiredToken = createToken({ exp: Math.floor(Date.now() / 1000) - 3600 });
await expect(validateToken(expiredToken)).rejects.toThrow('expired');
});
it('should reject tokens with wrong issuer', async () => {
const wrongIssuer = createToken({ iss: 'https://evil.com' });
await expect(validateToken(wrongIssuer)).rejects.toThrow('issuer');
});
it('should reject none algorithm', async () => {
const noneAlg = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIn0.';
await expect(validateToken(noneAlg)).rejects.toThrow('algorithm');
});
});
Handles the full Supabase workflow from schema changes to deployment, with built-in security guardrails that catch common traps like RLS...
Comprehensive guides and best practices for Neon Serverless Postgres, covering setup, connection methods, authentication, and platform APIs.
Guide for setting up and using Firebase Authentication. Use this skill when the user's app requires user sign-in, user management, or secure data access using auth rules.
A skill to evaluate how secure Firestore security rules are. Use this when Firestore security rules are updated to ensure that the generated rules are extremely secure and robust.
Official skill for integrating Firebase AI Logic (Gemini API) into web applications. Covers setup, multimodal inference, structured output, and security.
Complete Better Auth server and client setup with database adapters, session management, plugins, and security configuration.
Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. "deploy to vercel", "set up vercel", "add environment variables to vercel".
Complete Cloudflare platform integration with decision trees for compute, storage, AI, networking, security, and infrastructure-as-code.
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.
Send, read, and manage Gmail messages, drafts, labels, and account settings.
Comprehensive website auditing across 230+ rules in 21 categories including SEO, performance, security, and accessibility.
Shared authentication, CLI syntax, and output formatting patterns for gws Google Workspace commands.
Discover your celebrity doppelgänger with StarByFace!
GeoSpy: Pricing, Features, FAQs, and Alternatives for AI Teams
Detect AI-generated voices to protect against audio fraud.
Ensure Your Content's Originality with AI Plagiarism Checker
Upscale images by 400% without quality loss
Protect your social media from copyright disputes
Accurately Detect AI-Generated Content with TheChecker.AI
Chrome extension that detects and flags AI-generated content