Files
gh-jeremylongshore-claude-c…/commands/auth-setup.md
2025-11-30 08:20:34 +08:00

422 lines
10 KiB
Markdown

---
description: Generate authentication boilerplate with JWT, OAuth, and session support
shortcut: as
category: backend
difficulty: intermediate
estimated_time: 5-10 minutes
---
# Auth Setup
Generates complete authentication boilerplate including JWT, OAuth (Google/GitHub), session management, and password reset flows.
## What This Command Does
**Generated Auth System:**
- JWT authentication with refresh tokens
- OAuth2 (Google, GitHub, Facebook)
- Password hashing (bcrypt)
- Email verification
- Password reset flow
- Session management
- Rate limiting on auth endpoints
- Authentication middleware
**Output:** Complete authentication system ready for production
**Time:** 5-10 minutes
---
## Usage
```bash
# Generate full auth system
/auth-setup jwt
# Shortcut
/as oauth --providers google,github
# With specific features
/as jwt --features email-verification,password-reset,2fa
```
---
## Example Output
### **JWT Authentication**
**auth.service.ts:**
```typescript
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
import { User } from './models/User'
export class AuthService {
async register(email: string, password: string, name: string) {
// Check if user exists
const existing = await User.findOne({ email })
if (existing) {
throw new Error('Email already registered')
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 12)
// Create user
const user = await User.create({
email,
password: hashedPassword,
name,
emailVerified: false
})
// Generate verification token
const verificationToken = this.generateToken({ userId: user.id, type: 'verify' }, '24h')
// Send verification email (implement sendEmail)
await this.sendVerificationEmail(email, verificationToken)
// Generate auth tokens
const accessToken = this.generateAccessToken(user)
const refreshToken = this.generateRefreshToken(user)
return {
user: { id: user.id, email: user.email, name: user.name },
accessToken,
refreshToken
}
}
async login(email: string, password: string) {
const user = await User.findOne({ email })
if (!user) {
throw new Error('Invalid credentials')
}
const validPassword = await bcrypt.compare(password, user.password)
if (!validPassword) {
throw new Error('Invalid credentials')
}
if (!user.emailVerified) {
throw new Error('Please verify your email')
}
const accessToken = this.generateAccessToken(user)
const refreshToken = this.generateRefreshToken(user)
return {
user: { id: user.id, email: user.email, name: user.name },
accessToken,
refreshToken
}
}
async refreshToken(refreshToken: string) {
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!) as any
const user = await User.findById(decoded.userId)
if (!user) {
throw new Error('User not found')
}
const accessToken = this.generateAccessToken(user)
return { accessToken }
} catch (error) {
throw new Error('Invalid refresh token')
}
}
async verifyEmail(token: string) {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
if (decoded.type !== 'verify') {
throw new Error('Invalid token type')
}
await User.findByIdAndUpdate(decoded.userId, { emailVerified: true })
return { message: 'Email verified successfully' }
}
async requestPasswordReset(email: string) {
const user = await User.findOne({ email })
if (!user) {
// Don't reveal if user exists
return { message: 'If email exists, reset link sent' }
}
const resetToken = this.generateToken({ userId: user.id, type: 'reset' }, '1h')
await this.sendPasswordResetEmail(email, resetToken)
return { message: 'If email exists, reset link sent' }
}
async resetPassword(token: string, newPassword: string) {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
if (decoded.type !== 'reset') {
throw new Error('Invalid token type')
}
const hashedPassword = await bcrypt.hash(newPassword, 12)
await User.findByIdAndUpdate(decoded.userId, { password: hashedPassword })
return { message: 'Password reset successfully' }
}
private generateAccessToken(user: any) {
return jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
)
}
private generateRefreshToken(user: any) {
return jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: '7d' }
)
}
private generateToken(payload: any, expiresIn: string) {
return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn })
}
private async sendVerificationEmail(email: string, token: string) {
// Implement with SendGrid, Resend, etc.
}
private async sendPasswordResetEmail(email: string, token: string) {
// Implement with SendGrid, Resend, etc.
}
}
```
### **OAuth2 Setup (Google)**
**oauth.controller.ts:**
```typescript
import { OAuth2Client } from 'google-auth-library'
const googleClient = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI
)
export class OAuthController {
async googleLogin(req: Request, res: Response) {
const authUrl = googleClient.generateAuthUrl({
access_type: 'offline',
scope: ['profile', 'email']
})
res.redirect(authUrl)
}
async googleCallback(req: Request, res: Response) {
const { code } = req.query
const { tokens } = await googleClient.getToken(code as string)
googleClient.setCredentials(tokens)
const ticket = await googleClient.verifyIdToken({
idToken: tokens.id_token!,
audience: process.env.GOOGLE_CLIENT_ID
})
const payload = ticket.getPayload()
if (!payload) {
throw new Error('Invalid token')
}
// Find or create user
let user = await User.findOne({ email: payload.email })
if (!user) {
user = await User.create({
email: payload.email,
name: payload.name,
avatar: payload.picture,
emailVerified: true,
provider: 'google',
providerId: payload.sub
})
}
// Generate tokens
const accessToken = generateAccessToken(user)
const refreshToken = generateRefreshToken(user)
// Redirect with tokens
res.redirect(`/auth/success?token=${accessToken}&refresh=${refreshToken}`)
}
}
```
### **Authentication Middleware**
**auth.middleware.ts:**
```typescript
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'
declare global {
namespace Express {
interface Request {
user?: {
userId: string
email: string
}
}
}
}
export async function authenticate(req: Request, res: Response, next: NextFunction) {
try {
const authHeader = req.headers.authorization
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' })
}
const token = authHeader.split(' ')[1]
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any
req.user = {
userId: decoded.userId,
email: decoded.email
}
next()
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: 'Token expired' })
}
return res.status(401).json({ error: 'Invalid token' })
}
}
export function authorize(...roles: string[]) {
return async (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' })
}
const user = await User.findById(req.user.userId)
if (!user || !roles.includes(user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' })
}
next()
}
}
```
### **Rate Limiting**
```typescript
import rateLimit from 'express-rate-limit'
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 requests per window
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false
})
// Usage
app.post('/api/auth/login', authLimiter, authController.login)
```
---
## Environment Variables
```bash
# JWT
JWT_SECRET=your-super-secret-key-min-32-chars
JWT_REFRESH_SECRET=your-refresh-secret-key
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# OAuth - Google
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/google/callback
# OAuth - GitHub
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_REDIRECT_URI=http://localhost:3000/api/auth/github/callback
# Email
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASSWORD=your-sendgrid-api-key
FROM_EMAIL=[email protected]
```
---
## API Routes
```typescript
// routes/auth.routes.ts
import { Router } from 'express'
import { AuthController } from '../controllers/auth.controller'
import { authenticate } from '../middleware/auth.middleware'
import { authLimiter } from '../middleware/rate-limit'
const router = Router()
const authController = new AuthController()
// Registration & Login
router.post('/register', authController.register)
router.post('/login', authLimiter, authController.login)
router.post('/refresh', authController.refreshToken)
router.post('/logout', authenticate, authController.logout)
// Email Verification
router.post('/verify-email', authController.verifyEmail)
router.post('/resend-verification', authController.resendVerification)
// Password Reset
router.post('/forgot-password', authLimiter, authController.forgotPassword)
router.post('/reset-password', authController.resetPassword)
// OAuth
router.get('/google', authController.googleLogin)
router.get('/google/callback', authController.googleCallback)
router.get('/github', authController.githubLogin)
router.get('/github/callback', authController.githubCallback)
// Profile
router.get('/me', authenticate, authController.getProfile)
router.patch('/me', authenticate, authController.updateProfile)
router.post('/change-password', authenticate, authController.changePassword)
export default router
```
---
## Related Commands
- `/env-config-setup` - Generate environment config
- `/express-api-scaffold` - Generate Express API
- `/fastapi-scaffold` - Generate FastAPI
---
**Secure authentication. Easy integration. Production-ready.**