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
JavaScript
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
};