All files / backend/src/api/routes auth-routes.ts

0% Statements 0/55
0% Branches 0/28
0% Functions 0/6
0% Lines 0/55

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122                                                                                                                                                                                                                                                   
/**
 * Auth API Routes
 *
 * Public routes for login, refresh, and auth status check.
 * Protected routes for logout and password change.
 * These are registered BEFORE the auth middleware in the pipeline.
 */
 
import { Router, type Request, type Response } from 'express';
import type { AuthProvider } from '../../auth/types.js';
import type { TokenService } from '../../auth/token-service.js';
import { createAuthMiddleware } from '../middleware/auth-middleware.js';
 
export function createAuthRoutes(authProvider: AuthProvider, tokenService: TokenService): Router {
  const router = Router();
  const authMiddleware = createAuthMiddleware(tokenService);
 
  // GET /api/auth/check - public, tells frontend if auth is enabled
  router.get('/api/auth/check', (_req: Request, res: Response) => {
    res.json({ authEnabled: true });
  });
 
  // POST /api/auth/login - public
  router.post('/api/auth/login', async (req: Request, res: Response) => {
    try {
      const { email, password } = req.body;
 
      if (!email || !password) {
        res.status(400).json({ error: 'Email and password are required' });
        return;
      }
 
      const result = await authProvider.authenticate({ email, password });
 
      if (!result.success || !result.user) {
        res.status(401).json({ error: result.error || 'Invalid credentials' });
        return;
      }
 
      const token = tokenService.issueAccessToken(result.user.id, result.user.email);
      const refreshToken = tokenService.issueRefreshToken(result.user.id, result.user.email);
 
      res.json({
        token,
        refreshToken,
        user: result.user,
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Login failed';
      res.status(500).json({ error: message });
    }
  });
 
  // POST /api/auth/refresh - public
  router.post('/api/auth/refresh', async (req: Request, res: Response) => {
    try {
      const { refreshToken } = req.body;
 
      if (!refreshToken) {
        res.status(400).json({ error: 'Refresh token is required' });
        return;
      }
 
      const payload = tokenService.verifyToken(refreshToken, 'refresh');
      const user = await authProvider.getUserById(payload.userId);
 
      if (!user) {
        res.status(401).json({ error: 'User not found' });
        return;
      }
 
      // Blacklist old refresh token
      tokenService.blacklistToken(refreshToken);
 
      const newToken = tokenService.issueAccessToken(user.id, user.email);
      const newRefreshToken = tokenService.issueRefreshToken(user.id, user.email);
 
      res.json({
        token: newToken,
        refreshToken: newRefreshToken,
      });
    } catch {
      res.status(401).json({ error: 'Invalid refresh token' });
    }
  });
 
  // POST /api/auth/logout - requires valid token
  router.post('/api/auth/logout', authMiddleware, (req: Request, res: Response) => {
    const token = req.headers.authorization?.replace('Bearer ', '') || (req.query.token as string);
    if (token) {
      tokenService.blacklistToken(token);
    }
    res.json({ success: true });
  });
 
  // POST /api/auth/change-password - requires valid token
  router.post('/api/auth/change-password', authMiddleware, async (req: Request, res: Response) => {
    try {
      const { oldPassword, newPassword } = req.body;
      const userId = req.auth?.userId;
 
      if (!userId) {
        res.status(401).json({ error: 'Unauthorized' });
        return;
      }
 
      if (!oldPassword || !newPassword) {
        res.status(400).json({ error: 'Old and new passwords are required' });
        return;
      }
 
      await authProvider.changePassword(userId, oldPassword, newPassword);
      res.json({ success: true });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Password change failed';
      res.status(400).json({ error: message });
    }
  });
 
  return router;
}