Imported from trackmind-ai/sdlc-atlas (
plugins/sdlc-atlas/stacks/node/skills/jwt-auth/SKILL.md). Install upstream withnpx skills add trackmind-ai/sdlc-atlas --skill jwt-auth. Copyright stays with the author.
- Algorithm: RS256 with
fs.readFileSyncfor key pair. HS256 only if CLAUDE.md explicitly specifies. - Sign:
jwt.sign(payload, privateKey, { algorithm: 'RS256', expiresIn: '15m', issuer: APP_URL }). - Verify:
jwt.verify(token, publicKey, { algorithms: ['RS256'], issuer: APP_URL })in middleware. - Access token payload:
{ sub: userId, role, iat, exp }only. No sensitive data in payload. - Refresh token: opaque random string (
crypto.randomBytes(64).toString('hex')), stored hashed in DB with expiry. - Storage: HttpOnly + Secure + SameSite=Strict cookie. Never localStorage or sessionStorage.
authenticatemiddleware: extract fromAuthorization: Bearer <token>or cookie → verify →req.user = decoded.authorize(roles: string[])middleware:if (!roles.includes(req.user.role)) throw new AppError('Forbidden', 403).- Refresh endpoint: validate refresh token hash in DB → check expiry → issue new access token → rotate refresh token.
- Revocation:
SET revoked:<jti> EX <ttl>in Redis. Check inauthenticatemiddleware before returning user. - Password:
await bcrypt.hash(password, 12)on register.await bcrypt.compare(password, hash)on login. Never sync. - Rate limit login endpoint: 5 attempts per 15 minutes per IP with
express-rate-limit. - Tests: valid token 200, expired token 401, tampered token 401, wrong role 403, revoked token 401.
- Never log tokens or passwords — log
userIdonly. - Token rotation on every refresh — old refresh token immediately invalidated.