UNPKG

saget-auth-middleware

Version:

A comprehensive authentication middleware for Node.js applications with SSO integration, JWT validation, and role-based access control

514 lines (421 loc) 13.3 kB
import fs from 'fs'; import path from 'path'; import chalk from 'chalk'; export async function generateTSMiddleware(outputDir, config) { const { framework } = config; if (framework === 'next') { generateNextTSMiddleware(outputDir); } else if (framework === 'express') { generateExpressTSMiddleware(outputDir); } // Generate common utilities and types generateTypes(outputDir); generateUtilities(outputDir); generateReadme(outputDir, config); } function generateTypes(outputDir) { const typesContent = `// Type definitions for SAGET Auth Middleware export interface User { id: string; email: string; name: string; roles?: string[]; [key: string]: any; } export interface AuthConfig { publicPaths: string[]; protectedPaths: string[]; sso: { baseUrl: string; clientId: string; clientSecret: string; redirectUri: string; scope?: string; responseType?: string; grantType?: string; }; tokenRefreshThreshold?: number; enableLogging?: boolean; rbac?: { enabled: boolean; roles: Record<string, string[]>; }; } export interface AuthRequest extends Request { user?: User; isAuthenticated?: boolean; } export interface AuthResponse extends Response { // Additional response properties if needed } export interface TokenPayload { user: User; exp: number; iat: number; [key: string]: any; } export interface AuthHeaders { Authorization?: string; [key: string]: string | undefined; } `; const typesPath = path.join(outputDir, 'types.ts'); fs.writeFileSync(typesPath, typesContent); console.log(chalk.green('✅ Created types.ts')); } function generateNextTSMiddleware(outputDir) { const middlewareContent = `import { NextRequest, NextResponse } from 'next/server'; import { middleware as sagetMiddleware, configureMiddleware } from 'saget-auth-middleware'; import type { AuthConfig } from './types'; // Configure the middleware with your settings const authConfig: AuthConfig = { // Public paths that don't require authentication publicPaths: [ '/', '/login', '/register', '/api/health', '/favicon.ico', '/_next/static', '/_next/image' ], // Protected API routes protectedPaths: [ '/api/protected', '/dashboard', '/profile' ], // SSO Configuration (from environment variables) sso: { baseUrl: process.env.SSO_BASE_URL!, clientId: process.env.SSO_CLIENT_ID!, clientSecret: process.env.SSO_CLIENT_SECRET!, redirectUri: process.env.SSO_REDIRECT_URI!, scope: process.env.SSO_SCOPE || 'openid profile email', responseType: process.env.SSO_RESPONSE_TYPE || 'code', grantType: process.env.SSO_GRANT_TYPE || 'authorization_code' }, // Optional: Custom configuration tokenRefreshThreshold: 300, // Refresh token 5 minutes before expiry enableLogging: process.env.NODE_ENV === 'development', // Role-based access control (optional) rbac: { enabled: true, roles: { admin: ['/admin', '/api/admin'], user: ['/dashboard', '/profile'], guest: ['/'] } } }; // Configure and export the middleware export const middleware = configureMiddleware(authConfig); // Specify which paths should be processed by the middleware export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - api/auth (authentication endpoints) * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) */ '/((?!api/auth|_next/static|_next/image|favicon.ico).*)', ], }; `; const filePath = path.join(outputDir, 'middleware.ts'); fs.writeFileSync(filePath, middlewareContent); console.log(chalk.green('✅ Created Next.js middleware.ts')); } function generateExpressTSMiddleware(outputDir) { const middlewareContent = `import { Request, Response, NextFunction, Express } from 'express'; import { configureMiddleware } from 'saget-auth-middleware'; import type { AuthConfig, AuthRequest, User } from './types'; // Configure the middleware with your settings const authConfig: AuthConfig = { // Public paths that don't require authentication publicPaths: [ '/', '/login', '/register', '/api/health', '/favicon.ico' ], // Protected API routes protectedPaths: [ '/api/protected', '/dashboard', '/profile' ], // SSO Configuration (from environment variables) sso: { baseUrl: process.env.SSO_BASE_URL!, clientId: process.env.SSO_CLIENT_ID!, clientSecret: process.env.SSO_CLIENT_SECRET!, redirectUri: process.env.SSO_REDIRECT_URI!, scope: process.env.SSO_SCOPE || 'openid profile email', responseType: process.env.SSO_RESPONSE_TYPE || 'code', grantType: process.env.SSO_GRANT_TYPE || 'authorization_code' }, // Optional: Custom configuration tokenRefreshThreshold: 300, // Refresh token 5 minutes before expiry enableLogging: process.env.NODE_ENV === 'development', // Role-based access control (optional) rbac: { enabled: true, roles: { admin: ['/admin', '/api/admin'], user: ['/dashboard', '/profile'], guest: ['/'] } } }; // Create the configured middleware export const authMiddleware = configureMiddleware(authConfig); // Example Express.js setup with TypeScript export function setupAuth(app: Express): void { // Apply authentication middleware to all routes app.use(authMiddleware); // Example protected route app.get('/api/protected', (req: AuthRequest, res: Response) => { // User is authenticated at this point const user: User | undefined = req.user; res.json({ message: 'This is a protected route', user: user }); }); // Example admin-only route app.get('/api/admin', (req: AuthRequest, res: Response) => { // User is authenticated and has admin role const user: User | undefined = req.user; res.json({ message: 'Admin access granted', user: user }); }); } // Type-safe middleware wrapper export function requireAuth(req: AuthRequest, res: Response, next: NextFunction): void { if (!req.user) { res.status(401).json({ error: 'Authentication required' }); return; } next(); } // Role-based middleware export function requireRole(role: string) { return (req: AuthRequest, res: Response, next: NextFunction): void => { if (!req.user) { res.status(401).json({ error: 'Authentication required' }); return; } if (!req.user.roles?.includes(role)) { res.status(403).json({ error: 'Insufficient permissions' }); return; } next(); }; } `; const filePath = path.join(outputDir, 'auth-middleware.ts'); fs.writeFileSync(filePath, middlewareContent); console.log(chalk.green('✅ Created Express.js auth-middleware.ts')); // Create Express setup example const setupContent = `import express, { Express } from 'express'; import { setupAuth } from './auth-middleware'; const app: Express = express(); // Setup authentication middleware setupAuth(app); // Your other routes here app.get('/', (req, res) => { res.json({ message: 'Welcome to your authenticated app!' }); }); const PORT: number = parseInt(process.env.PORT || '3000', 10); app.listen(PORT, () => { console.log(\`Server running on port \${PORT}\`); }); `; const setupPath = path.join(outputDir, 'app-example.ts'); fs.writeFileSync(setupPath, setupContent); console.log(chalk.green('✅ Created Express.js app-example.ts')); } function generateUtilities(outputDir) { const utilsContent = `// Authentication utilities for client-side usage with TypeScript import type { User, AuthHeaders } from './types'; /** * Check if user is authenticated */ export function isAuthenticated(): boolean { if (typeof window === 'undefined') return false; const token = localStorage.getItem('access_token'); if (!token) return false; try { const payload = JSON.parse(atob(token.split('.')[1])); return payload.exp * 1000 > Date.now(); } catch { return false; } } /** * Get current user from token */ export function getCurrentUser(): User | null { if (typeof window === 'undefined') return null; const token = localStorage.getItem('access_token'); if (!token) return null; try { const payload = JSON.parse(atob(token.split('.')[1])); return payload.user || null; } catch { return null; } } /** * Logout user */ export function logout(): void { if (typeof window === 'undefined') return; localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); window.location.href = '/login'; } /** * Get authentication headers for API calls */ export function getAuthHeaders(): AuthHeaders { if (typeof window === 'undefined') return {}; const token = localStorage.getItem('access_token'); if (!token) return {}; return { 'Authorization': \`Bearer \${token}\` }; } /** * Check if user has specific role */ export function hasRole(role: string): boolean { const user = getCurrentUser(); return user?.roles?.includes(role) ?? false; } /** * Check if user has any of the specified roles */ export function hasAnyRole(roles: string[]): boolean { const user = getCurrentUser(); if (!user?.roles) return false; return roles.some(role => user.roles!.includes(role)); } /** * Get user roles */ export function getUserRoles(): string[] { const user = getCurrentUser(); return user?.roles ?? []; } `; const utilsPath = path.join(outputDir, 'auth-utils.ts'); fs.writeFileSync(utilsPath, utilsContent); console.log(chalk.green('✅ Created auth-utils.ts')); } function generateReadme(outputDir, config) { const readmeContent = `# SAGET Auth Middleware (TypeScript) Generated TypeScript authentication middleware for your ${config.framework === 'next' ? 'Next.js' : 'Express.js'} project. ## Setup 1. Install the middleware package: \`\`\`bash npm install saget-auth-middleware \`\`\` 2. Install TypeScript dependencies: \`\`\`bash npm install -D typescript @types/node \`\`\` 3. Configure your environment variables in \`.env\`: \`\`\`env SSO_BASE_URL=https://your-sso-server.com SSO_CLIENT_ID=your-client-id SSO_CLIENT_SECRET=your-client-secret SSO_REDIRECT_URI=http://localhost:3000/auth/callback \`\`\` ${config.framework === 'next' ? ` ## Next.js Usage The middleware is automatically configured in \`middleware.ts\`. It will: - Protect all routes except those in \`publicPaths\` - Handle authentication redirects - Manage token refresh automatically - Provide user context in your pages ### Using in Pages/Components \`\`\`typescript import { getCurrentUser, isAuthenticated, hasRole } from './middleware/auth-utils'; import type { User } from './middleware/types'; export default function Dashboard() { const user: User | null = getCurrentUser(); if (!isAuthenticated()) { return <div>Please log in</div>; } return ( <div> <h1>Welcome, {user?.name}!</h1> {hasRole('admin') && <AdminPanel />} </div> ); } \`\`\` ` : ` ## Express.js Usage Import and use the middleware in your Express app: \`\`\`typescript import express, { Express } from 'express'; import { setupAuth, requireRole } from './middleware/auth-middleware'; import type { AuthRequest } from './middleware/types'; const app: Express = express(); // Setup authentication setupAuth(app); // Protected route with role requirement app.get('/admin', requireRole('admin'), (req: AuthRequest, res) => { res.json({ message: 'Admin only content', user: req.user }); }); app.listen(3000); \`\`\` ### Manual Setup \`\`\`typescript import { authMiddleware, requireAuth } from './middleware/auth-middleware'; app.use(authMiddleware); app.use('/protected', requireAuth); \`\`\` `} ## Type Safety This TypeScript version provides: - **Type-safe configuration**: \`AuthConfig\` interface ensures correct setup - **User types**: \`User\` interface for consistent user data structure - **Request augmentation**: \`AuthRequest\` extends Express Request with user data - **Utility functions**: Type-safe helper functions for authentication checks - **Role-based helpers**: Type-safe role checking utilities ## Configuration You can customize the middleware by editing the configuration object in the generated files: - \`publicPaths\`: Routes that don't require authentication - \`protectedPaths\`: Routes that require authentication - \`rbac\`: Role-based access control settings - \`tokenRefreshThreshold\`: When to refresh tokens (seconds before expiry) ## Features - ✅ Full TypeScript support - ✅ JWT token validation - ✅ Automatic token refresh - ✅ Role-based access control (RBAC) - ✅ Configurable public/protected paths - ✅ SSO integration - ✅ Development logging - ✅ Type-safe client-side utilities - ✅ Express request augmentation ## Documentation For more information, visit: https://github.com/your-org/saget-auth-middleware `; const readmePath = path.join(outputDir, 'README.md'); fs.writeFileSync(readmePath, readmeContent); console.log(chalk.green('✅ Created README.md')); } // Default export for easier importing export default { generate: generateTSMiddleware };