UNPKG

mya-cli

Version:

MYA - AI-Powered Stock & Options Analysis CLI Tool

257 lines 10.6 kB
/** * Authentication manager for coordinating authentication operations * Acts as a facade to simplify authentication operations and maintain backward compatibility */ /** * Authentication manager singleton * Single responsibility: Provide unified access to authentication operations */ export class AuthManager { static instance; /** * Create a new auth manager * @param authProvider Authentication provider */ constructor() { // Intentionally left blank (no authProvider initialization) } /** * Get the singleton instance of AuthManager * @returns AuthManager instance */ static getInstance() { if (!AuthManager.instance) { // ...existing code... // Create and store the instance AuthManager.instance = new AuthManager(); } return AuthManager.instance; } get baseUrl() { return process.env.MYA_API_URL || 'http://localhost:8787'; } /** * Send a one-time code to the provided email * @param email Email to send code to * @returns Response with method ID */ async sendOtpCode(email) { try { const response = await fetch(`${this.baseUrl}/auth`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }); if (!response.ok) { // Try to get the error message from the response body let errorMessage = `Request failed with status ${response.status} ${response.statusText}`; try { const errorBody = await response.json(); if (typeof errorBody === 'object' && errorBody !== null && 'message' in errorBody && typeof errorBody.message === 'string') { errorMessage += `: ${errorBody.message}`; } } catch { // Ignore if we can't parse the error body } throw new Error(`Failed to send verification code: ${errorMessage}`); } const data = await response.json(); if (typeof data !== 'object' || data === null || !('method_id' in data) || typeof data.method_id !== 'string') { throw new Error('Invalid response from backend'); } return { methodId: data.method_id, method_id: data.method_id, user_id: typeof data.user_id === 'string' ? data.user_id : '', status_code: typeof data.status_code === 'number' ? data.status_code : 0, }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to send verification code: ${error.message}`); } throw new Error('Failed to send verification code: Unknown error'); } } /** * Logout the user by clearing the token from the backend * @returns Success status */ async logout() { try { const backendUrl = process.env.MYA_API_URL || 'http://localhost:8787'; const response = await fetch(`${backendUrl}/auth/logout`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); if (!response.ok) { throw new Error(`Failed to logout: ${response.status} ${response.statusText}`); } const data = await response.json(); if (typeof data !== 'object' || data === null || !('success' in data) || typeof data.success !== 'boolean' || !('message' in data) || typeof data.message !== 'string') { throw new Error('Invalid response from backend'); } return data; } catch (err) { if (err instanceof Error && err.message.includes('Failed to fetch')) { throw new Error('Network error: Unable to connect to authentication service'); } else if (err instanceof Error) { throw new Error(`Logout error: ${err.message || 'Unknown error'}`); } throw new Error('Logout error: Unknown error'); } } /** * Check authentication status from the backend * @returns Authentication status information */ async checkAuthStatus() { try { const backendUrl = process.env.MYA_API_URL || 'http://localhost:8787'; const response = await fetch(`${backendUrl}/auth/status`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, }); if (!response.ok) { throw new Error(`Failed to check auth status: ${response.status} ${response.statusText}`); } const data = await response.json(); if (typeof data !== 'object' || data === null || !('authenticated' in data) || typeof data.authenticated !== 'boolean') { throw new Error('Invalid response from backend'); } // Optionally check user and expiresAt if ('user' in data && data.user !== undefined && (typeof data.user !== 'object' || data.user === null || !('id' in data.user) || typeof data.user.id !== 'string' || !('email' in data.user) || typeof data.user.email !== 'string')) { throw new Error('Invalid user object in response'); } if ('expiresAt' in data && data.expiresAt !== undefined && typeof data.expiresAt !== 'number') { throw new Error('Invalid expiresAt in response'); } return data; } catch (err) { if (err instanceof Error && err.message.includes('Failed to fetch')) { throw new Error('Network error: Unable to connect to authentication service'); } else if (err instanceof Error) { throw new Error(`Authentication status error: ${err.message || 'Unknown error'}`); } throw new Error('Authentication status error: Unknown error'); } } /** * Verify a one-time code * @param methodId Method ID from send operation * @param code OTP code received by user * @param email Email associated with the OTP * @returns Session information upon successful verification */ async verifyOtpCode(methodId, code, email) { try { // Attempt to contact backend to verify OTP const backendUrl = process.env.MYA_API_URL || 'http://localhost:8787'; const response = await fetch(`${backendUrl}/auth/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ method_id: methodId, code, email }), }); // Debug: Check for Set-Cookie header const setCookieHeader = response.headers.get('Set-Cookie'); // Extract and save the session cookie if (setCookieHeader) { // No-op: Do not persist cookies to disk. All session/auth state should be handled via KV cache. } if (!response.ok) { // Try to get the error message from the response let errorMessage = `Failed to verify OTP: ${response.status} ${response.statusText}`; try { const errorData = await response.json(); if (typeof errorData === 'object' && errorData !== null && 'error' in errorData && typeof errorData.error === 'string') { errorMessage = errorData.error; } } catch { // Ignore JSON parse error } throw new Error(errorMessage); } const data = await response.json(); if (typeof data !== 'object' || data === null || !('token' in data) || typeof data.token !== 'string' || !('user' in data) || typeof data.user !== 'object' || data.user === null || typeof data.user !== 'object' || data.user === null || !('id' in data.user) || typeof (data.user.id) !== 'string') { throw new Error('Invalid response from backend'); } // Return the complete response data including token and user info return { session_token: data.token || '', user_id: (data.user.id) || '', token: data.token, email: typeof (data.user.email) === 'string' ? (data.user.email) : email, expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days from now }; } catch (err) { if (err instanceof Error && err.message.includes('Failed to fetch')) { throw new Error('Network error: Unable to connect to authentication service'); } else if (err instanceof Error && err.message.includes('otp_code_not_found')) { throw new Error('otp_code_not_found'); } else if (err instanceof Error && err.message.includes('Invalid response')) { throw new Error('Invalid response from authentication service'); } else if (err instanceof Error) { throw new Error(`Authentication error: ${err.message || 'Unknown error'}`); } throw new Error('Authentication error: Unknown error'); } } /** * Set a custom authentication provider * Used primarily for testing or alternative implementations * @param provider Authentication provider implementation */ setAuthProvider(_provider) { // Intentionally left blank (no authProvider assignment) } } //# sourceMappingURL=auth-manager.js.map