UNPKG

@dbs-portal/tool-mock

Version:

API mocking toolkit using MSW for DBS Portal development workflows

240 lines 8.84 kB
/** * Authentication handler factory for creating auth-related endpoints */ import { http, HttpResponse } from 'msw'; import { applyDelay } from '../utils/delay'; import { simulateError } from '../utils/error'; import { getMockConfig } from '../core/config'; /** * Simple JWT token generator (for mocking purposes only) */ function generateMockToken(user, expiresIn = '1h') { const header = { alg: 'HS256', typ: 'JWT' }; const payload = { sub: user.id, email: user.email, roles: user.roles || [], permissions: user.permissions || [], iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + parseExpiryTime(expiresIn), }; // Simple base64 encoding (not secure, for mocking only) const encodedHeader = btoa(JSON.stringify(header)); const encodedPayload = btoa(JSON.stringify(payload)); const signature = btoa(`mock-signature-${user.id}`); return `${encodedHeader}.${encodedPayload}.${signature}`; } /** * Parse expiry time string to seconds */ function parseExpiryTime(expiry) { const match = expiry.match(/^(\d+)([smhd])$/); if (!match) return 3600; // Default 1 hour const value = parseInt(match[1] || '0', 10); const unit = match[2]; switch (unit) { case 's': return value; case 'm': return value * 60; case 'h': return value * 3600; case 'd': return value * 86400; default: return 3600; } } /** * Validate JWT token (mock implementation) */ function validateMockToken(token) { try { const parts = token.split('.'); if (parts.length !== 3) return { valid: false }; const payload = JSON.parse(atob(parts[1] || '')); const now = Math.floor(Date.now() / 1000); if (payload.exp && payload.exp < now) { return { valid: false }; } return { valid: true, payload }; } catch { return { valid: false }; } } /** * Create authentication handlers */ export function createAuthHandlers(basePath, options = {}) { const config = getMockConfig(); const users = options.users || []; const tokenExpiry = options.tokenExpiry || '1h'; const refreshTokenExpiry = options.refreshTokenExpiry || '7d'; // Helper function to apply delay and error simulation const withMiddleware = async (request, method) => { const errorResponse = simulateError(request.url, method, config.errorSimulation); if (errorResponse) return errorResponse; const delay = config.delay; if (delay) await applyDelay(delay); return null; }; // Helper function to create response const createResponse = (data, status = 200) => { return HttpResponse.json({ success: true, data, timestamp: new Date().toISOString(), }, { status }); }; // Helper function to create error response const createErrorResponse = (error, status = 400) => { return HttpResponse.json({ success: false, error, timestamp: new Date().toISOString(), }, { status }); }; // Find user by email and password const findUser = (email, password) => { if (options.userValidator) { return options.userValidator({ email, password }); } return users.find(user => user.email === email && user.password === password && user.isActive !== false) || null; }; // Get user from token const getUserFromToken = (token) => { const validation = validateMockToken(token); if (!validation.valid || !validation.payload) return null; return users.find(user => user.id === validation.payload.sub) || null; }; const handlers = []; // POST /auth/login - User authentication handlers.push(http.post(`${basePath}/login`, async ({ request }) => { const middlewareResponse = await withMiddleware(request, 'POST'); if (middlewareResponse) return middlewareResponse; try { const credentials = (await request.json()); if (!credentials.email || !credentials.password) { return createErrorResponse({ code: 'MISSING_CREDENTIALS', message: 'Email and password are required', }, 400); } const user = findUser(credentials.email, credentials.password); if (!user) { return createErrorResponse({ code: 'INVALID_CREDENTIALS', message: 'Invalid email or password', }, 401); } const token = options.tokenGenerator ? options.tokenGenerator(user) : generateMockToken(user, tokenExpiry); const refreshToken = generateMockToken(user, refreshTokenExpiry); const authResponse = { user: { ...user, password: undefined, // Remove password from response }, token, refreshToken, expiresIn: parseExpiryTime(tokenExpiry), }; return createResponse(authResponse); } catch (error) { return createErrorResponse({ code: 'INVALID_JSON', message: 'Invalid JSON in request body', }, 400); } })); // POST /auth/logout - User logout handlers.push(http.post(`${basePath}/logout`, async ({ request }) => { const middlewareResponse = await withMiddleware(request, 'POST'); if (middlewareResponse) return middlewareResponse; // In a real implementation, you'd invalidate the token return createResponse({ message: 'Logged out successfully' }); })); // POST /auth/refresh - Token refresh handlers.push(http.post(`${basePath}/refresh`, async ({ request }) => { const middlewareResponse = await withMiddleware(request, 'POST'); if (middlewareResponse) return middlewareResponse; try { const { refreshToken } = (await request.json()); if (!refreshToken) { return createErrorResponse({ code: 'MISSING_REFRESH_TOKEN', message: 'Refresh token is required', }, 400); } const validation = validateMockToken(refreshToken); if (!validation.valid) { return createErrorResponse({ code: 'INVALID_REFRESH_TOKEN', message: 'Invalid or expired refresh token', }, 401); } const user = getUserFromToken(refreshToken); if (!user) { return createErrorResponse({ code: 'USER_NOT_FOUND', message: 'User not found', }, 401); } const token = options.tokenGenerator ? options.tokenGenerator(user) : generateMockToken(user, tokenExpiry); const newRefreshToken = generateMockToken(user, refreshTokenExpiry); const authResponse = { user: { ...user, password: undefined, }, token: token, refreshToken: newRefreshToken, expiresIn: parseExpiryTime(tokenExpiry), }; return createResponse(authResponse); } catch (error) { return createErrorResponse({ code: 'INVALID_JSON', message: 'Invalid JSON in request body', }, 400); } })); // GET /auth/me - Get current user info handlers.push(http.get(`${basePath}/me`, async ({ request }) => { const middlewareResponse = await withMiddleware(request, 'GET'); if (middlewareResponse) return middlewareResponse; const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return createErrorResponse({ code: 'UNAUTHORIZED', message: 'Authorization header is required', }, 401); } const token = authHeader.substring(7); const user = getUserFromToken(token); if (!user) { return createErrorResponse({ code: 'INVALID_TOKEN', message: 'Invalid or expired token', }, 401); } return createResponse({ ...user, password: undefined, }); })); return handlers; } //# sourceMappingURL=auth.js.map