@dbs-portal/tool-mock
Version:
API mocking toolkit using MSW for DBS Portal development workflows
240 lines • 8.84 kB
JavaScript
/**
* 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