UNPKG

ndwallet-core

Version:

Core cryptographic library for NDWallet browser environments

250 lines (249 loc) 8.52 kB
import axios from 'axios'; import { getLocalData, KEY_AUTH_TOKEN } from '../storage'; let serverConfig = null; /** * Normalize a string by converting it to lowercase and trimming whitespace * @param value - The string to normalize * @returns The normalized string */ function _normalizeString(value) { return value.toLowerCase().trim(); } /** * Set server configuration for user module * @param config - Configuration object containing server URL and API key */ export function setServerConfig(config) { serverConfig = config; } /** * Check if a user exists by email * @param email - The user's email address * @returns Promise resolving to a boolean indicating if the user exists */ export async function checkUserExists(email) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { const response = await axios.get(`${serverConfig.serverUrl}/users/check-email`, { params: { email }, headers: { 'X-API-KEY': serverConfig.apiKey } }); return response.data.exists; } catch (error) { console.error('Error checking user existence:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to check user existence: ${String(error)}`); } } } /** * Start the authentication process for a user * @param email - The user's email address * @param credentialId - The user's credential ID (optional) * @param deviceToken - The device token for the user (optional) * @returns Authentication options and server data */ export async function startUserAuthentication(email, credentialId, deviceToken) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { const response = await axios.post(`${serverConfig.serverUrl}/auth/authenticate/start`, { email, credentialId, deviceToken }); return response.data; } catch (error) { console.error('Error starting authentication:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to start authentication: ${String(error)}`); } } } /** * Complete the authentication process * @param credentialId - The user's credential ID * @param authenticationResponse - The WebAuthn response from the authenticator * @returns Authentication completion data */ export async function completeUserAuthentication(credentialId, authenticationResponse) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { const serverResponse = await axios.post(`${serverConfig.serverUrl}/auth/authenticate/complete`, { credentialId, authenticationResponse }); return serverResponse.data; } catch (error) { console.error('Error completing authentication:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to complete authentication: ${String(error)}`); } } } /** * Start the registration process for a new user * @param email - The user's email address * @param serverShare - The encrypted share for the server data * @param backupShare - The encrypted share for the backup data * @param secretQuestionId - The security question ID (optional) * @param deviceToken - The device token for the user (optional) * @returns Registration options and server data */ export async function startUserRegistration(email, serverShare, backupShare, secretQuestionId, deviceToken) { if (!serverConfig) { throw new Error('User configuration not set. Please call setUserConfig first.'); } try { const response = await axios.post(`${serverConfig.serverUrl}/auth/register/start`, { email, serverShare, ipfsShare: backupShare, secretQuestionId, deviceToken }); return response.data; } catch (error) { console.error('Error starting registration:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to start registration: ${String(error)}`); } } } /** * Complete the registration process * @param email - The user's email address * @param deviceServerShare - The encrypted share for the server data (using device passkey) * @param registrationResponse - The WebAuthn response from the authenticator * @param prfSalt - The PRF salt used for key derivation * @returns Registration completion data */ export async function completeUserRegistration(email, deviceId, deviceServerShare, registrationResponse, prfSalt) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { const serverResponse = await axios.post(`${serverConfig.serverUrl}/auth/register/complete`, { email, deviceId, deviceServerShare, registrationResponse, prfSalt }); return serverResponse.data; } catch (error) { console.error('Error completing registration:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to complete registration: ${String(error)}`); } } } /** * Fetch user data. * @param authToken - The user's authentication token * @returns User data including credential details and encrypted server share */ async function _fetchUserData(authToken) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { const response = await axios.get(`${serverConfig.serverUrl}/users/me`, { headers: { 'X-API-KEY': serverConfig.apiKey, 'Authorization': `Bearer ${authToken}` } }); return response.data; } catch (error) { console.error('Error fetching user by token:', error); if (error instanceof Error) { throw error; } else { throw new Error(`Failed to fetch user by token: ${String(error)}`); } } } /** * Get user data. * @param email - The user's email address * @returns User data including credential details and encrypted server share */ export async function getUserData(email) { const authToken = getLocalData(email, KEY_AUTH_TOKEN); if (!authToken) { throw new Error('No authentication token found'); } const userData = await _fetchUserData(authToken); const { user, credential } = userData; return { id: user.id, email: user.email, credential }; } /** * Fetch the list of secret questions from the server * @returns Promise resolving to an array of SecretQuestion objects */ export async function allSecretQuestions() { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } // Make the API request using axios const response = await axios.get(`${serverConfig.serverUrl}/auth/secret-questions/`, { headers: { 'X-API-KEY': serverConfig.apiKey } }); // Return the questions return response.data; } /** * Get the secret question for a specific user * @param email - The user's email address * @returns The secret question object or null if not found */ export async function getSecretQuestion(email) { if (!serverConfig) { throw new Error('Server configuration not set. Please call setServerConfig first.'); } try { // Normalize email to lowercase const normalizedEmail = _normalizeString(email); // Make the request to get the question for this email const response = await axios.post(`${serverConfig.serverUrl}/auth/recovery/get-question`, { email: normalizedEmail }, { headers: { 'X-API-KEY': serverConfig.apiKey, 'Content-Type': 'application/json' } }); // Check if we got a valid response with a question return response.data; } catch (error) { console.error('Error fetching secret question:', error); return null; } }