- Authentication Setup
- When to use this skill
- Lists specific situations where this skill should be triggered:
- User Login System
-
- When adding user authentication to a new application
- API Security
-
- When adding an authentication layer to a REST or GraphQL API
- Permission Management
-
- When role-based access control is needed
- Authentication Migration
-
- When migrating an existing auth system to JWT or OAuth
- SSO Integration
-
- When integrating social login with Google, GitHub, Microsoft, etc.
- Input Format
- The required and optional input information to collect from the user:
- Required Information
- Authentication Method
-
- Choose from JWT, Session, or OAuth 2.0
- Backend Framework
-
- Express, Django, FastAPI, Spring Boot, etc.
- Database
-
- PostgreSQL, MySQL, MongoDB, etc.
- Security Requirements
-
- Password policy, token expiry times, etc.
- Optional Information
- MFA Support
-
- Whether to enable 2FA/MFA (default: false)
- Social Login
-
- OAuth providers (Google, GitHub, etc.)
- Session Storage
-
- Redis, in-memory, etc. (if using sessions)
- Refresh Token
- Whether to use (default: true)
Input Example
Build a user authentication system:
- Auth method: JWT
- Framework: Express.js + TypeScript
- Database: PostgreSQL
- MFA: Google Authenticator support
- Social login: Google, GitHub
- Refresh Token: enabled
Instructions
Specifies the step-by-step task sequence to follow precisely.
Step 1: Design the Data Model
Design the database schema for users and authentication.
Tasks
:
Design the User table (id, email, password_hash, role, created_at, updated_at)
RefreshToken table (optional)
OAuthProvider table (if using social login)
Never store passwords in plaintext (bcrypt/argon2 hashing is mandatory)
Example
(PostgreSQL):
CREATE
TABLE
users
(
id UUID
PRIMARY
KEY
DEFAULT
gen_random_uuid
(
)
,
email
VARCHAR
(
255
)
UNIQUE
NOT
NULL
,
password_hash
VARCHAR
(
255
)
,
-- NULL if OAuth only
role
VARCHAR
(
50
)
DEFAULT
'user'
,
is_verified
BOOLEAN
DEFAULT
false
,
mfa_secret
VARCHAR
(
255
)
,
created_at
TIMESTAMP
DEFAULT
NOW
(
)
,
updated_at
TIMESTAMP
DEFAULT
NOW
(
)
)
;
CREATE
TABLE
refresh_tokens
(
id UUID
PRIMARY
KEY
DEFAULT
gen_random_uuid
(
)
,
user_id UUID
REFERENCES
users
(
id
)
ON
DELETE
CASCADE
,
token
VARCHAR
(
500
)
UNIQUE
NOT
NULL
,
expires_at
TIMESTAMP
NOT
NULL
,
created_at
TIMESTAMP
DEFAULT
NOW
(
)
)
;
CREATE
INDEX
idx_users_email
ON
users
(
email
)
;
CREATE
INDEX
idx_refresh_tokens_user_id
ON
refresh_tokens
(
user_id
)
;
Step 2: Implement Password Security
Implement password hashing and verification logic.
Tasks
:
Use bcrypt (Node.js) or argon2 (Python)
Set salt rounds to a minimum of 10
Password strength validation (minimum 8 chars, upper/lowercase, numbers, special characters)
Decision Criteria
:
Node.js projects → use the bcrypt library
Python projects → use argon2-cffi or passlib
Performance-critical cases → choose bcrypt
Cases requiring maximum security → choose argon2
Example
(Node.js + TypeScript):
import
bcrypt
from
'bcrypt'
;
const
SALT_ROUNDS
=
12
;
export
async
function
hashPassword
(
password
:
string
)
:
Promise
<
string
{ // Validate password strength if ( password . length < 8 ) { throw new Error ( 'Password must be at least 8 characters' ) ; } const hasUpperCase = / [ A - Z ] / . test ( password ) ; const hasLowerCase = / [ a - z ] / . test ( password ) ; const hasNumber = / \d / . test ( password ) ; const hasSpecial = / [ !@#$%^&*(),.?":{}|<> ] / . test ( password ) ; if ( ! hasUpperCase || ! hasLowerCase || ! hasNumber || ! hasSpecial ) { throw new Error ( 'Password must contain uppercase, lowercase, number, and special character' ) ; } return await bcrypt . hash ( password , SALT_ROUNDS ) ; } export async function verifyPassword ( password : string , hash : string ) : Promise < boolean
{ return await bcrypt . compare ( password , hash ) ; } Step 3: Generate and Verify JWT Tokens Implement a token system for JWT-based authentication. Tasks : Access Token (short expiry: 15 minutes) Refresh Token (long expiry: 7–30 days) Use a strong SECRET key for JWT signing (manage via environment variables) Include only the minimum necessary information in the token payload (user_id, role) Example (Node.js): import jwt from 'jsonwebtoken' ; const ACCESS_TOKEN_SECRET = process . env . ACCESS_TOKEN_SECRET ! ; const REFRESH_TOKEN_SECRET = process . env . REFRESH_TOKEN_SECRET ! ; const ACCESS_TOKEN_EXPIRY = '15m' ; const REFRESH_TOKEN_EXPIRY = '7d' ; interface TokenPayload { userId : string ; email : string ; role : string ; } export function generateAccessToken ( payload : TokenPayload ) : string { return jwt . sign ( payload , ACCESS_TOKEN_SECRET , { expiresIn : ACCESS_TOKEN_EXPIRY , issuer : 'your-app-name' , audience : 'your-app-users' } ) ; } export function generateRefreshToken ( payload : TokenPayload ) : string { return jwt . sign ( payload , REFRESH_TOKEN_SECRET , { expiresIn : REFRESH_TOKEN_EXPIRY , issuer : 'your-app-name' , audience : 'your-app-users' } ) ; } export function verifyAccessToken ( token : string ) : TokenPayload { return jwt . verify ( token , ACCESS_TOKEN_SECRET , { issuer : 'your-app-name' , audience : 'your-app-users' } ) as TokenPayload ; } export function verifyRefreshToken ( token : string ) : TokenPayload { return jwt . verify ( token , REFRESH_TOKEN_SECRET , { issuer : 'your-app-name' , audience : 'your-app-users' } ) as TokenPayload ; } Step 4: Implement Authentication Middleware Write authentication middleware to protect API requests. Checklist : Extract Bearer token from the Authorization header Verify token and check expiry Attach user info to req.user for valid tokens Error handling (401 Unauthorized) Example (Express.js): import { Request , Response , NextFunction } from 'express' ; import { verifyAccessToken } from './jwt' ; export interface AuthRequest extends Request { user ? : { userId : string ; email : string ; role : string ; } ; } export function authenticateToken ( req : AuthRequest , res : Response , next : NextFunction ) { const authHeader = req . headers [ 'authorization' ] ; const token = authHeader && authHeader . split ( ' ' ) [ 1 ] ; // Bearer TOKEN if ( ! token ) { return res . status ( 401 ) . json ( { error : 'Access token required' } ) ; } try { const payload = verifyAccessToken ( token ) ; req . user = payload ; next ( ) ; } catch ( error ) { if ( error . name === 'TokenExpiredError' ) { return res . status ( 401 ) . json ( { error : 'Token expired' } ) ; } return res . status ( 403 ) . json ( { error : 'Invalid token' } ) ; } } // Role-based authorization middleware export function requireRole ( ... roles : string [ ] ) { return ( req : AuthRequest , res : Response , next : NextFunction ) => { if ( ! req . user ) { return res . status ( 401 ) . json ( { error : 'Authentication required' } ) ; } if ( ! roles . includes ( req . user . role ) ) { return res . status ( 403 ) . json ( { error : 'Insufficient permissions' } ) ; } next ( ) ; } ; } Step 5: Implement Authentication API Endpoints Write APIs for registration, login, token refresh, etc. Tasks : POST /auth/register - registration POST /auth/login - login POST /auth/refresh - token refresh POST /auth/logout - logout GET /auth/me - current user info Example : import express from 'express' ; import { hashPassword , verifyPassword } from './password' ; import { generateAccessToken , generateRefreshToken , verifyRefreshToken } from './jwt' ; import { authenticateToken } from './middleware' ; const router = express . Router ( ) ; // Registration router . post ( '/register' , async ( req , res ) => { try { const { email , password } = req . body ; // Check for duplicate email const existingUser = await db . user . findUnique ( { where : { email } } ) ; if ( existingUser ) { return res . status ( 409 ) . json ( { error : 'Email already exists' } ) ; } // Hash the password const passwordHash = await hashPassword ( password ) ; // Create the user const user = await db . user . create ( { data : { email , password_hash : passwordHash , role : 'user' } } ) ; // Generate tokens const accessToken = generateAccessToken ( { userId : user . id , email : user . email , role : user . role } ) ; const refreshToken = generateRefreshToken ( { userId : user . id , email : user . email , role : user . role } ) ; // Store Refresh token in DB await db . refreshToken . create ( { data : { user_id : user . id , token : refreshToken , expires_at : new Date ( Date . now ( ) + 7 * 24 * 60 * 60 * 1000 ) // 7 days } } ) ; res . status ( 201 ) . json ( { user : { id : user . id , email : user . email , role : user . role } , accessToken , refreshToken } ) ; } catch ( error ) { res . status ( 500 ) . json ( { error : error . message } ) ; } } ) ; // Login router . post ( '/login' , async ( req , res ) => { try { const { email , password } = req . body ; // Find the user const user = await db . user . findUnique ( { where : { email } } ) ; if ( ! user || ! user . password_hash ) { return res . status ( 401 ) . json ( { error : 'Invalid credentials' } ) ; } // Verify the password const isValid = await verifyPassword ( password , user . password_hash ) ; if ( ! isValid ) { return res . status ( 401 ) . json ( { error : 'Invalid credentials' } ) ; } // Generate tokens const accessToken = generateAccessToken ( { userId : user . id , email : user . email , role : user . role } ) ; const refreshToken = generateRefreshToken ( { userId : user . id , email : user . email , role : user . role } ) ; // Store Refresh token await db . refreshToken . create ( { data : { user_id : user . id , token : refreshToken , expires_at : new Date ( Date . now ( ) + 7 * 24 * 60 * 60 * 1000 ) } } ) ; res . json ( { user : { id : user . id , email : user . email , role : user . role } , accessToken , refreshToken } ) ; } catch ( error ) { res . status ( 500 ) . json ( { error : error . message } ) ; } } ) ; // Token refresh router . post ( '/refresh' , async ( req , res ) => { try { const { refreshToken } = req . body ; if ( ! refreshToken ) { return res . status ( 401 ) . json ( { error : 'Refresh token required' } ) ; } // Verify Refresh token const payload = verifyRefreshToken ( refreshToken ) ; // Check token in DB const storedToken = await db . refreshToken . findUnique ( { where : { token : refreshToken } } ) ; if ( ! storedToken || storedToken . expires_at < new Date ( ) ) { return res . status ( 403 ) . json ( { error : 'Invalid or expired refresh token' } ) ; } // Generate new Access token const accessToken = generateAccessToken ( { userId : payload . userId , email : payload . email , role : payload . role } ) ; res . json ( { accessToken } ) ; } catch ( error ) { res . status ( 403 ) . json ( { error : 'Invalid refresh token' } ) ; } } ) ; // Current user info router . get ( '/me' , authenticateToken , async ( req : AuthRequest , res ) => { try { const user = await db . user . findUnique ( { where : { id : req . user ! . userId } , select : { id : true , email : true , role : true , created_at : true } } ) ; res . json ( { user } ) ; } catch ( error ) { res . status ( 500 ) . json ( { error : error . message } ) ; } } ) ; export default router ; Output format Defines the exact format that deliverables should follow. Basic Structure Project directory/ ├── src/ │ ├── auth/ │ │ ├── password.ts # password hashing/verification │ │ ├── jwt.ts # JWT token generation/verification │ │ ├── middleware.ts # authentication middleware │ │ └── routes.ts # authentication API endpoints │ ├── models/ │ │ └── User.ts # user model │ └── database/ │ └── schema.sql # database schema ├── .env.example # environment variable template └── README.md # authentication system documentation Environment Variable File (.env.example)
JWT Secrets (MUST change in production)
ACCESS_TOKEN_SECRET
your-access-token-secret-min-32-characters REFRESH_TOKEN_SECRET = your-refresh-token-secret-min-32-characters
Database
DATABASE_URL
postgresql://user:password@localhost:5432/myapp
OAuth (Optional)
GOOGLE_CLIENT_ID
- your-google-client-id
- GOOGLE_CLIENT_SECRET
- =
- your-google-client-secret
- GITHUB_CLIENT_ID
- =
- your-github-client-id
- GITHUB_CLIENT_SECRET
- =
- your-github-client-secret
- Constraints
- Specifies mandatory rules and prohibited actions.
- Mandatory Rules (MUST)
- Password Security
-
- Never store passwords in plaintext
- Use a proven hashing algorithm such as bcrypt or argon2
- Salt rounds minimum of 10
- Environment Variable Management
-
- Manage all secret keys via environment variables
- Add .env files to .gitignore
- Provide a list of required variables via .env.example
- Token Expiry
-
- Access Tokens should be short-lived (15 min), Refresh Tokens appropriately longer (7 days)
- Balance security and user experience
- Store Refresh Tokens in the DB to enable revocation
- Prohibited Actions (MUST NOT)
- Plaintext Passwords
-
- Never store passwords in plaintext or print them to logs
- Serious security risk
- Legal liability issues
- Hardcoding JWT SECRET
-
- Do not write SECRET keys directly in code
- Risk of being exposed on GitHub
- Production security vulnerability
- Sensitive Data in Tokens
-
- Do not include passwords, card numbers, or other sensitive data in JWT payloads
- JWT can be decoded (it is not encrypted)
- Include only the minimum information (user_id, role)
- Security Rules
- Rate Limiting
-
- Apply rate limiting to the login API (prevents brute-force attacks)
- HTTPS Required
-
- Use HTTPS only in production environments
- CORS Configuration
-
- Allow only approved domains to access the API
- Input Validation
-
- Validate all user input (prevents SQL Injection and XSS)
- Examples
- Demonstrates how to apply the skill through real-world use cases.
- Example 1: Express.js + PostgreSQL JWT Authentication
- Situation
-
- Adding JWT-based user authentication to a Node.js Express app
- User Request
- :
- Add JWT authentication to an Express.js app using PostgreSQL,
- with access token expiry of 15 minutes and refresh token expiry of 7 days.
- Skill Application Process
- :
- Install packages:
- npm
- install
- jsonwebtoken bcrypt pg
- npm
- install
- --save-dev @types/jsonwebtoken @types/bcrypt
- Create the database schema (use the SQL above)
- Set environment variables:
- ACCESS_TOKEN_SECRET
- =
- $(
- openssl rand
- -base64
- 32
- )
- REFRESH_TOKEN_SECRET
- =
- $(
- openssl rand
- -base64
- 32
- )
- Implement auth modules (use the code examples above)
- Connect API routes:
- import
- authRoutes
- from
- './auth/routes'
- ;
- app
- .
- use
- (
- '/api/auth'
- ,
- authRoutes
- )
- ;
- Final Result
-
- JWT-based authentication system complete, registration/login/token-refresh APIs working
- Example 2: Role-Based Access Control (RBAC)
- Situation
-
- A permission system that distinguishes administrators from regular users
- User Request
- :
- Create an API accessible only to administrators.
- Regular users should receive a 403 error.
- Final Result
- :
- // Admin-only API
- router
- .
- delete
- (
- '/users/:id'
- ,
- authenticateToken
- ,
- // verify authentication
- requireRole
- (
- 'admin'
- )
- ,
- // verify role
- async
- (
- req
- ,
- res
- )
- =>
- {
- // user deletion logic
- await
- db
- .
- user
- .
- delete
- (
- {
- where
- :
- {
- id
- :
- req
- .
- params
- .
- id
- }
- }
- )
- ;
- res
- .
- json
- (
- {
- message
- :
- 'User deleted'
- }
- )
- ;
- }
- )
- ;
- // Usage example
- // Regular user (role: 'user') request → 403 Forbidden
- // Admin (role: 'admin') request → 200 OK
- Best practices
- Recommendations for using this skill effectively.
- Quality Improvement
- Password Rotation Policy
-
- Recommend periodic password changes
- Change notification every 90 days
- Prevent reuse of the last 5 passwords
- Balance user experience and security
- Multi-Factor Authentication (MFA)
-
- Apply 2FA to important accounts
- Use TOTP apps such as Google Authenticator or Authy
- SMS is less secure (risk of SIM swapping)
- Provide backup codes
- Audit Logging
-
- Log all authentication events
- Record login success/failure, IP address, and User Agent
- Anomaly detection and post-incident analysis
- GDPR compliance (exclude sensitive data)
- Efficiency Improvements
- Token Blacklist
-
- Revoke Refresh Tokens on logout
- Redis Caching
-
- Cache frequently used user data
- Database Indexing
-
- Add indexes on email and refresh_token
- Common Issues
- Common problems and their solutions.
- Issue 1: "JsonWebTokenError: invalid signature"
- Symptom
- :
- Error occurs during token verification
- Login succeeds but authenticated API calls fail
- Cause
- :
- The SECRET keys for Access Token and Refresh Token are different,
- but the same key is being used to verify both.
- Solution
- :
- Check environment variables:
- ACCESS_TOKEN_SECRET
- ,
- REFRESH_TOKEN_SECRET
- Use the correct SECRET for each token type
- Verify that environment variables load correctly (initialize
- dotenv
- )
- Issue 2: Frontend Cannot Log In Due to CORS Error
- Symptom
-
- "CORS policy" error in the browser console
- Cause
-
- Missing CORS configuration on the Express server
- Solution
- :
- import
- cors
- from
- 'cors'
- ;
- app
- .
- use
- (
- cors
- (
- {
- origin
- :
- process
- .
- env
- .
- FRONTEND_URL
- ||
- 'http://localhost:3000'
- ,
- credentials
- :
- true
- }
- )
- )
- ;
- Issue 3: Refresh Token Keeps Expiring
- Symptom
-
- Users are frequently logged out
- Cause
-
- Refresh Token is not properly managed in the DB
- Solution
- :
- Confirm Refresh Token is saved to DB upon creation
- Set an appropriate expiry time (minimum 7 days)
- Add a cron job to regularly clean up expired tokens
- References
- Official Documentation
- JWT.io - JSON Web Token Introduction
- OWASP Authentication Cheat Sheet
- OAuth 2.0 RFC 6749
- Libraries
- jsonwebtoken (Node.js)
- bcrypt (Node.js)
- Passport.js
- - multiple authentication strategies
- NextAuth.js
- - Next.js authentication
- Security Guides
- OWASP Top 10
- NIST Digital Identity Guidelines
- Metadata
- Version
- Current Version
-
- 1.0.0
- Last Updated
-
- 2025-01-01
- Compatible Platforms
- Claude, ChatGPT, Gemini