安装
npx skills add https://github.com/wshobson/agents --skill auth-implementation-patterns
- Authentication & Authorization Implementation Patterns
- Build secure, scalable authentication and authorization systems using industry-standard patterns and modern best practices.
- When to Use This Skill
- Implementing user authentication systems
- Securing REST or GraphQL APIs
- Adding OAuth2/social login
- Implementing role-based access control (RBAC)
- Designing session management
- Migrating authentication systems
- Debugging auth issues
- Implementing SSO or multi-tenancy
- Core Concepts
- 1. Authentication vs Authorization
- Authentication (AuthN)
-
- Who are you?
- Verifying identity (username/password, OAuth, biometrics)
- Issuing credentials (sessions, tokens)
- Managing login/logout
- Authorization (AuthZ)
-
- What can you do?
- Permission checking
- Role-based access control (RBAC)
- Resource ownership validation
- Policy enforcement
- 2. Authentication Strategies
- Session-Based:
- Server stores session state
- Session ID in cookie
- Traditional, simple, stateful
- Token-Based (JWT):
- Stateless, self-contained
- Scales horizontally
- Can store claims
- OAuth2/OpenID Connect:
- Delegate authentication
- Social login (Google, GitHub)
- Enterprise SSO
- JWT Authentication
- Pattern 1: JWT Implementation
- // JWT structure: header.payload.signature
- import
- jwt
- from
- "jsonwebtoken"
- ;
- import
- {
- Request
- ,
- Response
- ,
- NextFunction
- }
- from
- "express"
- ;
- interface
- JWTPayload
- {
- userId
- :
- string
- ;
- email
- :
- string
- ;
- role
- :
- string
- ;
- iat
- :
- number
- ;
- exp
- :
- number
- ;
- }
- // Generate JWT
- function
- generateTokens
- (
- userId
- :
- string
- ,
- email
- :
- string
- ,
- role
- :
- string
- )
- {
- const
- accessToken
- =
- jwt
- .
- sign
- (
- {
- userId
- ,
- email
- ,
- role
- }
- ,
- process
- .
- env
- .
- JWT_SECRET
- !
- ,
- {
- expiresIn
- :
- "15m"
- }
- ,
- // Short-lived
- )
- ;
- const
- refreshToken
- =
- jwt
- .
- sign
- (
- {
- userId
- }
- ,
- process
- .
- env
- .
- JWT_REFRESH_SECRET
- !
- ,
- {
- expiresIn
- :
- "7d"
- }
- ,
- // Long-lived
- )
- ;
- return
- {
- accessToken
- ,
- refreshToken
- }
- ;
- }
- // Verify JWT
- function
- verifyToken
- (
- token
- :
- string
- )
- :
- JWTPayload
- {
- try
- {
- return
- jwt
- .
- verify
- (
- token
- ,
- process
- .
- env
- .
- JWT_SECRET
- !
- )
- as
- JWTPayload
- ;
- }
- catch
- (
- error
- )
- {
- if
- (
- error
- instanceof
- jwt
- .
- TokenExpiredError
- )
- {
- throw
- new
- Error
- (
- "Token expired"
- )
- ;
- }
- if
- (
- error
- instanceof
- jwt
- .
- JsonWebTokenError
- )
- {
- throw
- new
- Error
- (
- "Invalid token"
- )
- ;
- }
- throw
- error
- ;
- }
- }
- // Middleware
- function
- authenticate
- (
- req
- :
- Request
- ,
- res
- :
- Response
- ,
- next
- :
- NextFunction
- )
- {
- const
- authHeader
- =
- req
- .
- headers
- .
- authorization
- ;
- if
- (
- !
- authHeader
- ?.
- startsWith
- (
- "Bearer "
- )
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "No token provided"
- }
- )
- ;
- }
- const
- token
- =
- authHeader
- .
- substring
- (
- 7
- )
- ;
- try
- {
- const
- payload
- =
- verifyToken
- (
- token
- )
- ;
- req
- .
- user
- =
- payload
- ;
- // Attach user to request
- next
- (
- )
- ;
- }
- catch
- (
- error
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Invalid token"
- }
- )
- ;
- }
- }
- // Usage
- app
- .
- get
- (
- "/api/profile"
- ,
- authenticate
- ,
- (
- req
- ,
- res
- )
- =>
- {
- res
- .
- json
- (
- {
- user
- :
- req
- .
- user
- }
- )
- ;
- }
- )
- ;
- Pattern 2: Refresh Token Flow
- interface
- StoredRefreshToken
- {
- token
- :
- string
- ;
- userId
- :
- string
- ;
- expiresAt
- :
- Date
- ;
- createdAt
- :
- Date
- ;
- }
- class
- RefreshTokenService
- {
- // Store refresh token in database
- async
- storeRefreshToken
- (
- userId
- :
- string
- ,
- refreshToken
- :
- string
- )
- {
- const
- expiresAt
- =
- new
- Date
- (
- Date
- .
- now
- (
- )
- +
- 7
- *
- 24
- *
- 60
- *
- 60
- *
- 1000
- )
- ;
- await
- db
- .
- refreshTokens
- .
- create
- (
- {
- token
- :
- await
- hash
- (
- refreshToken
- )
- ,
- // Hash before storing
- userId
- ,
- expiresAt
- ,
- }
- )
- ;
- }
- // Refresh access token
- async
- refreshAccessToken
- (
- refreshToken
- :
- string
- )
- {
- // Verify refresh token
- let
- payload
- ;
- try
- {
- payload
- =
- jwt
- .
- verify
- (
- refreshToken
- ,
- process
- .
- env
- .
- JWT_REFRESH_SECRET
- !
- )
- as
- {
- userId
- :
- string
- ;
- }
- ;
- }
- catch
- {
- throw
- new
- Error
- (
- "Invalid refresh token"
- )
- ;
- }
- // Check if token exists in database
- const
- storedToken
- =
- await
- db
- .
- refreshTokens
- .
- findOne
- (
- {
- where
- :
- {
- token
- :
- await
- hash
- (
- refreshToken
- )
- ,
- userId
- :
- payload
- .
- userId
- ,
- expiresAt
- :
- {
- $gt
- :
- new
- Date
- (
- )
- }
- ,
- }
- ,
- }
- )
- ;
- if
- (
- !
- storedToken
- )
- {
- throw
- new
- Error
- (
- "Refresh token not found or expired"
- )
- ;
- }
- // Get user
- const
- user
- =
- await
- db
- .
- users
- .
- findById
- (
- payload
- .
- userId
- )
- ;
- if
- (
- !
- user
- )
- {
- throw
- new
- Error
- (
- "User not found"
- )
- ;
- }
- // Generate new access token
- const
- accessToken
- =
- jwt
- .
- sign
- (
- {
- userId
- :
- user
- .
- id
- ,
- email
- :
- user
- .
- email
- ,
- role
- :
- user
- .
- role
- }
- ,
- process
- .
- env
- .
- JWT_SECRET
- !
- ,
- {
- expiresIn
- :
- "15m"
- }
- ,
- )
- ;
- return
- {
- accessToken
- }
- ;
- }
- // Revoke refresh token (logout)
- async
- revokeRefreshToken
- (
- refreshToken
- :
- string
- )
- {
- await
- db
- .
- refreshTokens
- .
- deleteOne
- (
- {
- token
- :
- await
- hash
- (
- refreshToken
- )
- ,
- }
- )
- ;
- }
- // Revoke all user tokens (logout all devices)
- async
- revokeAllUserTokens
- (
- userId
- :
- string
- )
- {
- await
- db
- .
- refreshTokens
- .
- deleteMany
- (
- {
- userId
- }
- )
- ;
- }
- }
- // API endpoints
- app
- .
- post
- (
- "/api/auth/refresh"
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- const
- {
- refreshToken
- }
- =
- req
- .
- body
- ;
- try
- {
- const
- {
- accessToken
- }
- =
- await
- refreshTokenService
- .
- refreshAccessToken
- (
- refreshToken
- )
- ;
- res
- .
- json
- (
- {
- accessToken
- }
- )
- ;
- }
- catch
- (
- error
- )
- {
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Invalid refresh token"
- }
- )
- ;
- }
- }
- )
- ;
- app
- .
- post
- (
- "/api/auth/logout"
- ,
- authenticate
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- const
- {
- refreshToken
- }
- =
- req
- .
- body
- ;
- await
- refreshTokenService
- .
- revokeRefreshToken
- (
- refreshToken
- )
- ;
- res
- .
- json
- (
- {
- message
- :
- "Logged out successfully"
- }
- )
- ;
- }
- )
- ;
- Session-Based Authentication
- Pattern 1: Express Session
- import
- session
- from
- "express-session"
- ;
- import
- RedisStore
- from
- "connect-redis"
- ;
- import
- {
- createClient
- }
- from
- "redis"
- ;
- // Setup Redis for session storage
- const
- redisClient
- =
- createClient
- (
- {
- url
- :
- process
- .
- env
- .
- REDIS_URL
- ,
- }
- )
- ;
- await
- redisClient
- .
- connect
- (
- )
- ;
- app
- .
- use
- (
- session
- (
- {
- store
- :
- new
- RedisStore
- (
- {
- client
- :
- redisClient
- }
- )
- ,
- secret
- :
- process
- .
- env
- .
- SESSION_SECRET
- !
- ,
- resave
- :
- false
- ,
- saveUninitialized
- :
- false
- ,
- cookie
- :
- {
- secure
- :
- process
- .
- env
- .
- NODE_ENV
- ===
- "production"
- ,
- // HTTPS only
- httpOnly
- :
- true
- ,
- // No JavaScript access
- maxAge
- :
- 24
- *
- 60
- *
- 60
- *
- 1000
- ,
- // 24 hours
- sameSite
- :
- "strict"
- ,
- // CSRF protection
- }
- ,
- }
- )
- ,
- )
- ;
- // Login
- app
- .
- post
- (
- "/api/auth/login"
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- const
- {
- email
- ,
- password
- }
- =
- req
- .
- body
- ;
- const
- user
- =
- await
- db
- .
- users
- .
- findOne
- (
- {
- email
- }
- )
- ;
- if
- (
- !
- user
- ||
- !
- (
- await
- verifyPassword
- (
- password
- ,
- user
- .
- passwordHash
- )
- )
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Invalid credentials"
- }
- )
- ;
- }
- // Store user in session
- req
- .
- session
- .
- userId
- =
- user
- .
- id
- ;
- req
- .
- session
- .
- role
- =
- user
- .
- role
- ;
- res
- .
- json
- (
- {
- user
- :
- {
- id
- :
- user
- .
- id
- ,
- email
- :
- user
- .
- email
- ,
- role
- :
- user
- .
- role
- }
- }
- )
- ;
- }
- )
- ;
- // Session middleware
- function
- requireAuth
- (
- req
- :
- Request
- ,
- res
- :
- Response
- ,
- next
- :
- NextFunction
- )
- {
- if
- (
- !
- req
- .
- session
- .
- userId
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Not authenticated"
- }
- )
- ;
- }
- next
- (
- )
- ;
- }
- // Protected route
- app
- .
- get
- (
- "/api/profile"
- ,
- requireAuth
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- const
- user
- =
- await
- db
- .
- users
- .
- findById
- (
- req
- .
- session
- .
- userId
- )
- ;
- res
- .
- json
- (
- {
- user
- }
- )
- ;
- }
- )
- ;
- // Logout
- app
- .
- post
- (
- "/api/auth/logout"
- ,
- (
- req
- ,
- res
- )
- =>
- {
- req
- .
- session
- .
- destroy
- (
- (
- err
- )
- =>
- {
- if
- (
- err
- )
- {
- return
- res
- .
- status
- (
- 500
- )
- .
- json
- (
- {
- error
- :
- "Logout failed"
- }
- )
- ;
- }
- res
- .
- clearCookie
- (
- "connect.sid"
- )
- ;
- res
- .
- json
- (
- {
- message
- :
- "Logged out successfully"
- }
- )
- ;
- }
- )
- ;
- }
- )
- ;
- OAuth2 / Social Login
- Pattern 1: OAuth2 with Passport.js
- import
- passport
- from
- "passport"
- ;
- import
- {
- Strategy
- as
- GoogleStrategy
- }
- from
- "passport-google-oauth20"
- ;
- import
- {
- Strategy
- as
- GitHubStrategy
- }
- from
- "passport-github2"
- ;
- // Google OAuth
- passport
- .
- use
- (
- new
- GoogleStrategy
- (
- {
- clientID
- :
- process
- .
- env
- .
- GOOGLE_CLIENT_ID
- !
- ,
- clientSecret
- :
- process
- .
- env
- .
- GOOGLE_CLIENT_SECRET
- !
- ,
- callbackURL
- :
- "/api/auth/google/callback"
- ,
- }
- ,
- async
- (
- accessToken
- ,
- refreshToken
- ,
- profile
- ,
- done
- )
- =>
- {
- try
- {
- // Find or create user
- let
- user
- =
- await
- db
- .
- users
- .
- findOne
- (
- {
- googleId
- :
- profile
- .
- id
- ,
- }
- )
- ;
- if
- (
- !
- user
- )
- {
- user
- =
- await
- db
- .
- users
- .
- create
- (
- {
- googleId
- :
- profile
- .
- id
- ,
- email
- :
- profile
- .
- emails
- ?.
- [
- 0
- ]
- ?.
- value
- ,
- name
- :
- profile
- .
- displayName
- ,
- avatar
- :
- profile
- .
- photos
- ?.
- [
- 0
- ]
- ?.
- value
- ,
- }
- )
- ;
- }
- return
- done
- (
- null
- ,
- user
- )
- ;
- }
- catch
- (
- error
- )
- {
- return
- done
- (
- error
- ,
- undefined
- )
- ;
- }
- }
- ,
- )
- ,
- )
- ;
- // Routes
- app
- .
- get
- (
- "/api/auth/google"
- ,
- passport
- .
- authenticate
- (
- "google"
- ,
- {
- scope
- :
- [
- "profile"
- ,
- "email"
- ]
- ,
- }
- )
- ,
- )
- ;
- app
- .
- get
- (
- "/api/auth/google/callback"
- ,
- passport
- .
- authenticate
- (
- "google"
- ,
- {
- session
- :
- false
- }
- )
- ,
- (
- req
- ,
- res
- )
- =>
- {
- // Generate JWT
- const
- tokens
- =
- generateTokens
- (
- req
- .
- user
- .
- id
- ,
- req
- .
- user
- .
- email
- ,
- req
- .
- user
- .
- role
- )
- ;
- // Redirect to frontend with token
- res
- .
- redirect
- (
- `
- ${
- process
- .
- env
- .
- FRONTEND_URL
- }
- /auth/callback?token=
- ${
- tokens
- .
- accessToken
- }
- `
- ,
- )
- ;
- }
- ,
- )
- ;
- Authorization Patterns
- Pattern 1: Role-Based Access Control (RBAC)
- enum
- Role
- {
- USER
- =
- "user"
- ,
- MODERATOR
- =
- "moderator"
- ,
- ADMIN
- =
- "admin"
- ,
- }
- const
- roleHierarchy
- :
- Record
- <
- Role
- ,
- Role
- [
- ]
- >
- =
- {
- [
- Role
- .
- ADMIN
- ]
- :
- [
- Role
- .
- ADMIN
- ,
- Role
- .
- MODERATOR
- ,
- Role
- .
- USER
- ]
- ,
- [
- Role
- .
- MODERATOR
- ]
- :
- [
- Role
- .
- MODERATOR
- ,
- Role
- .
- USER
- ]
- ,
- [
- Role
- .
- USER
- ]
- :
- [
- Role
- .
- USER
- ]
- ,
- }
- ;
- function
- hasRole
- (
- userRole
- :
- Role
- ,
- requiredRole
- :
- Role
- )
- :
- boolean
- {
- return
- roleHierarchy
- [
- userRole
- ]
- .
- includes
- (
- requiredRole
- )
- ;
- }
- // Middleware
- function
- requireRole
- (
- ...
- roles
- :
- Role
- [
- ]
- )
- {
- return
- (
- req
- :
- Request
- ,
- res
- :
- Response
- ,
- next
- :
- NextFunction
- )
- =>
- {
- if
- (
- !
- req
- .
- user
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Not authenticated"
- }
- )
- ;
- }
- if
- (
- !
- roles
- .
- some
- (
- (
- role
- )
- =>
- hasRole
- (
- req
- .
- user
- .
- role
- ,
- role
- )
- )
- )
- {
- return
- res
- .
- status
- (
- 403
- )
- .
- json
- (
- {
- error
- :
- "Insufficient permissions"
- }
- )
- ;
- }
- next
- (
- )
- ;
- }
- ;
- }
- // Usage
- app
- .
- delete
- (
- "/api/users/:id"
- ,
- authenticate
- ,
- requireRole
- (
- Role
- .
- ADMIN
- )
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- // Only admins can delete users
- await
- db
- .
- users
- .
- delete
- (
- req
- .
- params
- .
- id
- )
- ;
- res
- .
- json
- (
- {
- message
- :
- "User deleted"
- }
- )
- ;
- }
- ,
- )
- ;
- Pattern 2: Permission-Based Access Control
- enum
- Permission
- {
- READ_USERS
- =
- "read:users"
- ,
- WRITE_USERS
- =
- "write:users"
- ,
- DELETE_USERS
- =
- "delete:users"
- ,
- READ_POSTS
- =
- "read:posts"
- ,
- WRITE_POSTS
- =
- "write:posts"
- ,
- }
- const
- rolePermissions
- :
- Record
- <
- Role
- ,
- Permission
- [
- ]
- >
- =
- {
- [
- Role
- .
- USER
- ]
- :
- [
- Permission
- .
- READ_POSTS
- ,
- Permission
- .
- WRITE_POSTS
- ]
- ,
- [
- Role
- .
- MODERATOR
- ]
- :
- [
- Permission
- .
- READ_POSTS
- ,
- Permission
- .
- WRITE_POSTS
- ,
- Permission
- .
- READ_USERS
- ,
- ]
- ,
- [
- Role
- .
- ADMIN
- ]
- :
- Object
- .
- values
- (
- Permission
- )
- ,
- }
- ;
- function
- hasPermission
- (
- userRole
- :
- Role
- ,
- permission
- :
- Permission
- )
- :
- boolean
- {
- return
- rolePermissions
- [
- userRole
- ]
- ?.
- includes
- (
- permission
- )
- ??
- false
- ;
- }
- function
- requirePermission
- (
- ...
- permissions
- :
- Permission
- [
- ]
- )
- {
- return
- (
- req
- :
- Request
- ,
- res
- :
- Response
- ,
- next
- :
- NextFunction
- )
- =>
- {
- if
- (
- !
- req
- .
- user
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Not authenticated"
- }
- )
- ;
- }
- const
- hasAllPermissions
- =
- permissions
- .
- every
- (
- (
- permission
- )
- =>
- hasPermission
- (
- req
- .
- user
- .
- role
- ,
- permission
- )
- ,
- )
- ;
- if
- (
- !
- hasAllPermissions
- )
- {
- return
- res
- .
- status
- (
- 403
- )
- .
- json
- (
- {
- error
- :
- "Insufficient permissions"
- }
- )
- ;
- }
- next
- (
- )
- ;
- }
- ;
- }
- // Usage
- app
- .
- get
- (
- "/api/users"
- ,
- authenticate
- ,
- requirePermission
- (
- Permission
- .
- READ_USERS
- )
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- const
- users
- =
- await
- db
- .
- users
- .
- findAll
- (
- )
- ;
- res
- .
- json
- (
- {
- users
- }
- )
- ;
- }
- ,
- )
- ;
- Pattern 3: Resource Ownership
- // Check if user owns resource
- async
- function
- requireOwnership
- (
- resourceType
- :
- "post"
- |
- "comment"
- ,
- resourceIdParam
- :
- string
- =
- "id"
- ,
- )
- {
- return
- async
- (
- req
- :
- Request
- ,
- res
- :
- Response
- ,
- next
- :
- NextFunction
- )
- =>
- {
- if
- (
- !
- req
- .
- user
- )
- {
- return
- res
- .
- status
- (
- 401
- )
- .
- json
- (
- {
- error
- :
- "Not authenticated"
- }
- )
- ;
- }
- const
- resourceId
- =
- req
- .
- params
- [
- resourceIdParam
- ]
- ;
- // Admins can access anything
- if
- (
- req
- .
- user
- .
- role
- ===
- Role
- .
- ADMIN
- )
- {
- return
- next
- (
- )
- ;
- }
- // Check ownership
- let
- resource
- ;
- if
- (
- resourceType
- ===
- "post"
- )
- {
- resource
- =
- await
- db
- .
- posts
- .
- findById
- (
- resourceId
- )
- ;
- }
- else
- if
- (
- resourceType
- ===
- "comment"
- )
- {
- resource
- =
- await
- db
- .
- comments
- .
- findById
- (
- resourceId
- )
- ;
- }
- if
- (
- !
- resource
- )
- {
- return
- res
- .
- status
- (
- 404
- )
- .
- json
- (
- {
- error
- :
- "Resource not found"
- }
- )
- ;
- }
- if
- (
- resource
- .
- userId
- !==
- req
- .
- user
- .
- userId
- )
- {
- return
- res
- .
- status
- (
- 403
- )
- .
- json
- (
- {
- error
- :
- "Not authorized"
- }
- )
- ;
- }
- next
- (
- )
- ;
- }
- ;
- }
- // Usage
- app
- .
- put
- (
- "/api/posts/:id"
- ,
- authenticate
- ,
- requireOwnership
- (
- "post"
- )
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- // User can only update their own posts
- const
- post
- =
- await
- db
- .
- posts
- .
- update
- (
- req
- .
- params
- .
- id
- ,
- req
- .
- body
- )
- ;
- res
- .
- json
- (
- {
- post
- }
- )
- ;
- }
- ,
- )
- ;
- Security Best Practices
- Pattern 1: Password Security
- import
- bcrypt
- from
- "bcrypt"
- ;
- import
- {
- z
- }
- from
- "zod"
- ;
- // Password validation schema
- const
- passwordSchema
- =
- z
- .
- string
- (
- )
- .
- min
- (
- 12
- ,
- "Password must be at least 12 characters"
- )
- .
- regex
- (
- /
- [
- A
- -
- Z
- ]
- /
- ,
- "Password must contain uppercase letter"
- )
- .
- regex
- (
- /
- [
- a
- -
- z
- ]
- /
- ,
- "Password must contain lowercase letter"
- )
- .
- regex
- (
- /
- [
- 0
- -
- 9
- ]
- /
- ,
- "Password must contain number"
- )
- .
- regex
- (
- /
- [
- ^
- A
- -
- Z
- a
- -
- z
- 0
- -
- 9
- ]
- /
- ,
- "Password must contain special character"
- )
- ;
- // Hash password
- async
- function
- hashPassword
- (
- password
- :
- string
- )
- :
- Promise
- <
- string
- >
- {
- const
- saltRounds
- =
- 12
- ;
- // 2^12 iterations
- return
- bcrypt
- .
- hash
- (
- password
- ,
- saltRounds
- )
- ;
- }
- // Verify password
- async
- function
- verifyPassword
- (
- password
- :
- string
- ,
- hash
- :
- string
- ,
- )
- :
- Promise
- <
- boolean
- >
- {
- return
- bcrypt
- .
- compare
- (
- password
- ,
- hash
- )
- ;
- }
- // Registration with password validation
- app
- .
- post
- (
- "/api/auth/register"
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- try
- {
- const
- {
- email
- ,
- password
- }
- =
- req
- .
- body
- ;
- // Validate password
- passwordSchema
- .
- parse
- (
- password
- )
- ;
- // Check if user exists
- const
- existingUser
- =
- await
- db
- .
- users
- .
- findOne
- (
- {
- email
- }
- )
- ;
- if
- (
- existingUser
- )
- {
- return
- res
- .
- status
- (
- 400
- )
- .
- json
- (
- {
- error
- :
- "Email already registered"
- }
- )
- ;
- }
- // Hash password
- const
- passwordHash
- =
- await
- hashPassword
- (
- password
- )
- ;
- // Create user
- const
- user
- =
- await
- db
- .
- users
- .
- create
- (
- {
- email
- ,
- passwordHash
- ,
- }
- )
- ;
- // Generate tokens
- const
- tokens
- =
- generateTokens
- (
- user
- .
- id
- ,
- user
- .
- email
- ,
- user
- .
- role
- )
- ;
- res
- .
- status
- (
- 201
- )
- .
- json
- (
- {
- user
- :
- {
- id
- :
- user
- .
- id
- ,
- email
- :
- user
- .
- email
- }
- ,
- ...
- tokens
- ,
- }
- )
- ;
- }
- catch
- (
- error
- )
- {
- if
- (
- error
- instanceof
- z
- .
- ZodError
- )
- {
- return
- res
- .
- status
- (
- 400
- )
- .
- json
- (
- {
- error
- :
- error
- .
- errors
- [
- 0
- ]
- .
- message
- }
- )
- ;
- }
- res
- .
- status
- (
- 500
- )
- .
- json
- (
- {
- error
- :
- "Registration failed"
- }
- )
- ;
- }
- }
- )
- ;
- Pattern 2: Rate Limiting
- import
- rateLimit
- from
- "express-rate-limit"
- ;
- import
- RedisStore
- from
- "rate-limit-redis"
- ;
- // Login rate limiter
- const
- loginLimiter
- =
- rateLimit
- (
- {
- store
- :
- new
- RedisStore
- (
- {
- client
- :
- redisClient
- }
- )
- ,
- windowMs
- :
- 15
- *
- 60
- *
- 1000
- ,
- // 15 minutes
- max
- :
- 5
- ,
- // 5 attempts
- message
- :
- "Too many login attempts, please try again later"
- ,
- standardHeaders
- :
- true
- ,
- legacyHeaders
- :
- false
- ,
- }
- )
- ;
- // API rate limiter
- const
- apiLimiter
- =
- rateLimit
- (
- {
- windowMs
- :
- 60
- *
- 1000
- ,
- // 1 minute
- max
- :
- 100
- ,
- // 100 requests per minute
- standardHeaders
- :
- true
- ,
- }
- )
- ;
- // Apply to routes
- app
- .
- post
- (
- "/api/auth/login"
- ,
- loginLimiter
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- // Login logic
- }
- )
- ;
- app
- .
- use
- (
- "/api/"
- ,
- apiLimiter
- )
- ;
- Best Practices
- Never Store Plain Passwords
-
- Always hash with bcrypt/argon2
- Use HTTPS
-
- Encrypt data in transit
- Short-Lived Access Tokens
-
- 15-30 minutes max
- Secure Cookies
-
- httpOnly, secure, sameSite flags
- Validate All Input
-
- Email format, password strength
- Rate Limit Auth Endpoints
-
- Prevent brute force attacks
- Implement CSRF Protection
-
- For session-based auth
- Rotate Secrets Regularly
-
- JWT secrets, session secrets
- Log Security Events
-
- Login attempts, failed auth
- Use MFA When Possible
-
- Extra security layer
- Common Pitfalls
- Weak Passwords
-
- Enforce strong password policies
- JWT in localStorage
-
- Vulnerable to XSS, use httpOnly cookies
- No Token Expiration
-
- Tokens should expire
- Client-Side Auth Checks Only
-
- Always validate server-side
- Insecure Password Reset
-
- Use secure tokens with expiration
- No Rate Limiting
-
- Vulnerable to brute force
- Trusting Client Data
- Always validate on server
← 返回排行榜