/** * Auth API Module * * Provides authentication-related API methods. * All methods accept plain objects and return domain entities. * * @layer Infrastructure - API Client */ import { type Session } from "@archer/api-interface"; import { LOGIN, REGISTER, REFRESH_TOKEN, LOGOUT, ME, SESSIONS_LIST, SESSION_DELETE, SESSIONS_DELETE_ALL, } from "@archer/api-interface/endpoints/customer-api"; import { apiClient } from '../../ApiClient'; import { User } from '../../../../domain/entities/User'; import type { AuthSession } from '../shared/types'; import { AuthRequestMapper, type LoginCredentials, type UserRegistrationData } from './mappers/AuthRequestMapper'; import { AuthResponseMapper } from './mappers/AuthResponseMapper'; import { AuthenticatedUserMapper } from './mappers/AuthenticatedUserMapper'; import { SessionResponseMapper } from './mappers/SessionResponseMapper'; import type { AuthenticatedUser } from '@/domain/entities/AuthenticatedUser'; /** * Auth API * * Authentication and session management API methods. * Uses mappers internally for schema transformations. */ export const authApi = { /** * Login with email and password * * @param credentials - Login credentials (email, password, optional 2FA code) * @returns Auth session with user and tokens * @throws ApiError if login fails * @throws Error if 2FA is required (check with requires2FA first) */ async login(credentials: LoginCredentials): Promise<{ user: User; session: AuthSession; authenticatedUser: AuthenticatedUser }> { // Map plain credentials to request schema const requestDto = AuthRequestMapper.toLoginRequest(credentials); // Make API call const response = await apiClient.post( LOGIN.path, requestDto ); // Check if 2FA is required if (AuthResponseMapper.requires2FA(response)) { const tempToken = AuthResponseMapper.getTempToken(response); throw new Error( `2FA verification required. Use the temporary token: ${tempToken}` ); } // Map to both the legacy `user`/`session` shape (preserved for older // callers) AND the canonical AuthenticatedUser the dashboard now persists. const session = AuthResponseMapper.toAuthSession(response); const authenticatedUser = AuthenticatedUserMapper.fromTokenResponse(response); return { ...session, authenticatedUser }; }, /** * Register a new user * * @param data - User registration data (email, password, optional full name) * @returns Created user entity * @throws ApiError if registration fails */ async register(data: UserRegistrationData): Promise { // Map plain data to request schema const requestDto = AuthRequestMapper.toRegisterRequest(data); // Make API call const response = await apiClient.post( REGISTER.path, requestDto ); // Map response to User domain entity return AuthResponseMapper.toUser(response); }, /** * Refresh access token using refresh token * * @param refreshToken - Refresh token * @returns New auth session with refreshed tokens * @throws ApiError if refresh fails */ async refreshToken(refreshToken: string): Promise<{ user: User; session: AuthSession }> { const response = await apiClient.post( REFRESH_TOKEN.path, { refreshToken } ); return AuthResponseMapper.toAuthSession(response); }, /** * Logout current session * * Invalidates the current access token. * * @returns void * @throws ApiError if logout fails */ async logout(): Promise { await apiClient.post( LOGOUT.path, {} ); }, /** * Get current user profile * * Fetches the authenticated user's profile. * * @returns User domain entity * @throws ApiError if request fails */ async getMe(): Promise { const response = await apiClient.get( ME.path ); return AuthResponseMapper.toUser(response); }, /** * Get all active sessions for current user * * Returns list of all active sessions across devices. * * @returns Array of Session objects with total count * @throws ApiError if request fails */ async getSessions(): Promise<{ sessions: Session[]; total: number }> { const response = await apiClient.get( SESSIONS_LIST.path ); return SessionResponseMapper.toSessionList(response); }, /** * Revoke a specific session * * Invalidates the session with the given ID. * If this is the current session, the user will be logged out. * * @param sessionId - ID of session to revoke * @returns void * @throws ApiError if revocation fails */ async revokeSession(sessionId: string): Promise { const path = SESSION_DELETE.path.replace(':id', sessionId); await apiClient.delete(path); }, /** * Revoke all sessions except current * * Logs out all other devices/sessions. * * @returns void * @throws ApiError if revocation fails */ async revokeAllOtherSessions(): Promise { await apiClient.delete( SESSIONS_DELETE_ALL.path ); }, };