diff --git a/frontend/src/mocks/handlers/overrides.ts b/frontend/src/mocks/handlers/overrides.ts index 453a6be..bb151e1 100644 --- a/frontend/src/mocks/handlers/overrides.ts +++ b/frontend/src/mocks/handlers/overrides.ts @@ -12,8 +12,11 @@ */ import { http, HttpResponse, delay } from 'msw'; +import { generateMockToken } from '../utils/tokens'; +import { validateCredentials, setCurrentUser, currentUser } from '../data/users'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000'; +const NETWORK_DELAY = 300; // ms - simulate realistic network delay /** * Custom handler overrides @@ -22,17 +25,80 @@ const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8 * Add custom implementations here as needed. */ export const overrideHandlers = [ - // Example: Custom error simulation for testing - // http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request }) => { - // // Simulate rate limiting 10% of the time - // if (Math.random() < 0.1) { - // return HttpResponse.json( - // { detail: 'Too many login attempts' }, - // { status: 429 } - // ); - // } - // // Otherwise, use generated handler (by not returning anything) - // }), + /** + * Login Override + * Custom handler to return proper JWT tokens and user data + */ + http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request }) => { + await delay(NETWORK_DELAY); - // Add your custom handlers here... + const body = (await request.json()) as any; + const user = validateCredentials(body.email, body.password); + + if (!user) { + return HttpResponse.json({ detail: 'Incorrect email or password' }, { status: 401 }); + } + + setCurrentUser(user); + + return HttpResponse.json({ + access_token: generateMockToken('access', user.id), + refresh_token: generateMockToken('refresh', user.id), + token_type: 'bearer', + expires_in: 900, + user: user, + }); + }), + + /** + * Register Override + * Custom handler to return proper JWT tokens and user data + */ + http.post(`${API_BASE_URL}/api/v1/auth/register`, async ({ request }) => { + await delay(NETWORK_DELAY); + + const body = (await request.json()) as any; + + const newUser = { + id: `new-user-${Date.now()}`, + email: body.email, + first_name: body.first_name, + last_name: body.last_name || null, + phone_number: body.phone_number || null, + is_active: true, + is_superuser: false, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + last_login: null, + organization_count: 0, + }; + + setCurrentUser(newUser); + + return HttpResponse.json({ + user: newUser, + access_token: generateMockToken('access', newUser.id), + refresh_token: generateMockToken('refresh', newUser.id), + token_type: 'bearer', + expires_in: 900, + }); + }), + + /** + * Refresh Token Override + * Custom handler to return proper JWT tokens + */ + http.post(`${API_BASE_URL}/api/v1/auth/refresh`, async ({ request }) => { + await delay(NETWORK_DELAY); + + // Use current user's ID if available, otherwise generate a generic token + const userId = currentUser?.id || 'refreshed-user'; + + return HttpResponse.json({ + access_token: generateMockToken('access', userId), + refresh_token: generateMockToken('refresh', userId), + token_type: 'bearer', + expires_in: 900, + }); + }), ]; diff --git a/frontend/src/mocks/utils/tokens.ts b/frontend/src/mocks/utils/tokens.ts new file mode 100644 index 0000000..6056340 --- /dev/null +++ b/frontend/src/mocks/utils/tokens.ts @@ -0,0 +1,28 @@ +/** + * MSW Token Generation Utilities + * + * Helper functions for generating mock JWT tokens in demo mode. + * Tokens follow proper JWT format to pass client-side validation. + */ + +/** + * Generate a mock JWT-like token for demo mode + * Format: header.payload.signature (3 parts separated by dots) + * + * @param type - Token type ('access' or 'refresh') + * @param userId - User ID to include in the token payload + * @returns JWT-formatted token string + */ +export function generateMockToken(type: 'access' | 'refresh', userId: string): string { + const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })); + const payload = btoa( + JSON.stringify({ + sub: userId, + type: type, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + (type === 'access' ? 900 : 2592000), + }) + ); + const signature = btoa(`demo-${type}-${userId}-${Date.now()}`); + return `${header}.${payload}.${signature}`; +}