Spaces:
Paused
Paused
| /** | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| * β AUTHENTICATION ROUTES β | |
| * β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£ | |
| * β Endpoints for user authentication and token management β | |
| * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| */ | |
| import { Router, Request, Response } from 'express'; | |
| import { | |
| generateToken, | |
| generateRefreshToken, | |
| verifyRefreshToken, | |
| AuthenticatedRequest, | |
| authenticate | |
| } from '../middleware/authMiddleware.js'; | |
| import { hyperLog } from '../services/HyperLog.js'; | |
| const router = Router(); | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Types | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| interface LoginRequest { | |
| email: string; | |
| password: string; | |
| } | |
| interface TokenResponse { | |
| accessToken: string; | |
| refreshToken: string; | |
| expiresIn: number; | |
| user: { | |
| id: string; | |
| email: string; | |
| name: string; | |
| roles: string[]; | |
| }; | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Demo Users (Replace with database in production) | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const DEMO_USERS: Record<string, { password: string; name: string; roles: string[] }> = { | |
| 'admin@widgetdc.local': { | |
| password: 'admin123', | |
| name: 'Admin User', | |
| roles: ['admin', 'user'] | |
| }, | |
| 'user@widgetdc.local': { | |
| password: 'user123', | |
| name: 'Regular User', | |
| roles: ['user'] | |
| }, | |
| 'agent@widgetdc.local': { | |
| password: 'agent123', | |
| name: 'AI Agent', | |
| roles: ['agent', 'service'] | |
| } | |
| }; | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Routes | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * POST /api/auth/login | |
| * Login with email and password | |
| */ | |
| router.post('/login', async (req: Request, res: Response) => { | |
| const { email, password } = req.body as LoginRequest; | |
| if (!email || !password) { | |
| return res.status(400).json({ | |
| error: 'Email and password required', | |
| code: 'MISSING_CREDENTIALS' | |
| }); | |
| } | |
| // Check user exists | |
| const user = DEMO_USERS[email.toLowerCase()]; | |
| if (!user || user.password !== password) { | |
| hyperLog.logEvent('AUTH_LOGIN_FAILED', { email, reason: 'Invalid credentials' }); | |
| return res.status(401).json({ | |
| error: 'Invalid email or password', | |
| code: 'INVALID_CREDENTIALS' | |
| }); | |
| } | |
| // Generate tokens | |
| const userId = email.toLowerCase().replace(/[^a-z0-9]/g, '_'); | |
| const accessToken = generateToken({ | |
| sub: userId, | |
| email: email.toLowerCase(), | |
| name: user.name, | |
| roles: user.roles | |
| }); | |
| const refreshToken = generateRefreshToken(userId); | |
| hyperLog.logEvent('AUTH_LOGIN_SUCCESS', { | |
| email, | |
| userId, | |
| roles: user.roles | |
| }); | |
| const response: TokenResponse = { | |
| accessToken, | |
| refreshToken, | |
| expiresIn: 86400, // 24 hours | |
| user: { | |
| id: userId, | |
| email: email.toLowerCase(), | |
| name: user.name, | |
| roles: user.roles | |
| } | |
| }; | |
| res.json(response); | |
| }); | |
| /** | |
| * POST /api/auth/refresh | |
| * Refresh access token using refresh token | |
| */ | |
| router.post('/refresh', async (req: Request, res: Response) => { | |
| const { refreshToken } = req.body; | |
| if (!refreshToken) { | |
| return res.status(400).json({ | |
| error: 'Refresh token required', | |
| code: 'MISSING_REFRESH_TOKEN' | |
| }); | |
| } | |
| const userId = verifyRefreshToken(refreshToken); | |
| if (!userId) { | |
| hyperLog.logEvent('AUTH_REFRESH_FAILED', { reason: 'Invalid refresh token' }); | |
| return res.status(401).json({ | |
| error: 'Invalid or expired refresh token', | |
| code: 'INVALID_REFRESH_TOKEN' | |
| }); | |
| } | |
| // Find user by ID (in demo, derive from userId) | |
| const email = Object.keys(DEMO_USERS).find( | |
| e => e.toLowerCase().replace(/[^a-z0-9]/g, '_') === userId | |
| ); | |
| const user = email ? DEMO_USERS[email] : null; | |
| if (!user || !email) { | |
| return res.status(401).json({ | |
| error: 'User not found', | |
| code: 'USER_NOT_FOUND' | |
| }); | |
| } | |
| // Generate new tokens | |
| const newAccessToken = generateToken({ | |
| sub: userId, | |
| email, | |
| name: user.name, | |
| roles: user.roles | |
| }); | |
| const newRefreshToken = generateRefreshToken(userId); | |
| hyperLog.logEvent('AUTH_REFRESH_SUCCESS', { userId }); | |
| res.json({ | |
| accessToken: newAccessToken, | |
| refreshToken: newRefreshToken, | |
| expiresIn: 86400 | |
| }); | |
| }); | |
| /** | |
| * POST /api/auth/logout | |
| * Invalidate refresh token (in production, add to blacklist) | |
| */ | |
| router.post('/logout', authenticate, async (req: AuthenticatedRequest, res: Response) => { | |
| const userId = req.user?.sub; | |
| // In production: Add refresh token to blacklist in Redis | |
| hyperLog.logEvent('AUTH_LOGOUT', { userId }); | |
| res.json({ success: true, message: 'Logged out successfully' }); | |
| }); | |
| /** | |
| * GET /api/auth/me | |
| * Get current user info | |
| */ | |
| router.get('/me', authenticate, async (req: AuthenticatedRequest, res: Response) => { | |
| if (!req.user) { | |
| return res.status(401).json({ error: 'Not authenticated' }); | |
| } | |
| res.json({ | |
| id: req.user.sub, | |
| email: req.user.email, | |
| name: req.user.name, | |
| roles: req.user.roles, | |
| authMethod: req.authMethod | |
| }); | |
| }); | |
| /** | |
| * GET /api/auth/status | |
| * Check authentication status (public endpoint) | |
| */ | |
| router.get('/status', (req: Request, res: Response) => { | |
| const authEnabled = process.env.AUTH_ENABLED !== 'false'; | |
| const devBypass = process.env.NODE_ENV === 'development' && process.env.AUTH_BYPASS === 'true'; | |
| res.json({ | |
| authEnabled, | |
| devBypass, | |
| mode: devBypass ? 'bypass' : (authEnabled ? 'enforced' : 'disabled'), | |
| hint: authEnabled ? 'Use POST /api/auth/login to obtain tokens' : 'Authentication is disabled' | |
| }); | |
| }); | |
| export default router; | |