UNPKG

@dispatch9/client-sdk

Version:

Official Node.js SDK for Dispatch9 API - Complete solution with email/phone client creation, order management, client management, and dual-method authentication

532 lines (454 loc) โ€ข 20.5 kB
require('dotenv').config(); const Dispatch9Client = require('../src/client'); /** * Client Authentication Example * Demonstrates the new client authentication features: * - Login with email address or phone number * - Multi-provider client handling * - First-time login OTP verification * - Profile management and session handling * - Comprehensive error handling and validation */ async function clientAuthenticationExample() { console.log('๐Ÿš€ Dispatch9 SDK - Client Authentication Example\n'); console.log('๐Ÿ” Demonstrating email and phone number login capabilities\n'); try { // Initialize the SDK (no API key needed for client auth) const dispatch9 = new Dispatch9Client({ baseURL: process.env.DISPATCH9_BASE_URL || 'https://api.dispatch9.com', debug: false }); console.log('โœ… SDK initialized successfully\n'); // ========================================== // PART 1: EMAIL AND PHONE LOGIN EXAMPLES // ========================================== console.log('๐Ÿ“ง PART 1: Email and Phone Number Authentication'); console.log('โ•'.repeat(60)); // Example credentials (replace with real test credentials) const testCredentials = { email: 'testclient@example.com', phone: '+1234567890', formattedPhone: '+1 (234) 567-8900', password: 'testPassword123' }; console.log('โš ๏ธ Note: Using example credentials. Replace with real test credentials for actual testing.\n'); // Example 1: Email Login console.log('๐Ÿ“ง Example 1: Email Address Login'); console.log('-'.repeat(40)); try { const emailLogin = await dispatch9.loginClient({ identifier: testCredentials.email, password: testCredentials.password }); if (emailLogin.success) { console.log('โœ… Email login successful!'); console.log(` Client ID: ${emailLogin.client.id}`); console.log(` Name: ${emailLogin.client.name}`); console.log(` Email: ${emailLogin.client.email}`); console.log(` First Login: ${emailLogin.client.isFirstLogin ? 'Yes' : 'No'}`); console.log(` Registration Status: ${emailLogin.client.registrationStatus}`); if (emailLogin.tokens) { console.log(' ๐Ÿ”‘ Authentication tokens received'); console.log(` Access Token Expires: ${emailLogin.tokens.access.expires}`); } } else if (emailLogin.requiresProviderSelection) { console.log('๐Ÿ”„ Multiple providers detected - see provider selection example below'); } else if (emailLogin.requiresOTP) { console.log('๐Ÿ“ฒ OTP verification required - see OTP example below'); } else { console.log('โŒ Email login failed:', emailLogin.message); } } catch (error) { console.log('โŒ Email login error:', error.message); } console.log(''); // Example 2: Phone Number Login console.log('๐Ÿ“ฑ Example 2: Phone Number Login'); console.log('-'.repeat(40)); try { const phoneLogin = await dispatch9.loginClient({ identifier: testCredentials.phone, password: testCredentials.password }); if (phoneLogin.success) { console.log('โœ… Phone login successful!'); console.log(` Client ID: ${phoneLogin.client.id}`); console.log(` Name: ${phoneLogin.client.name}`); console.log(` Phone: ${phoneLogin.client.phone}`); console.log(` Email: ${phoneLogin.client.email || 'Not provided'}`); } else { console.log('โŒ Phone login failed:', phoneLogin.message); } } catch (error) { console.log('โŒ Phone login error:', error.message); } console.log(''); // Example 3: Formatted Phone Number Login console.log('๐Ÿ“ž Example 3: Formatted Phone Number Login'); console.log('-'.repeat(40)); try { const formattedPhoneLogin = await dispatch9.loginClient({ identifier: testCredentials.formattedPhone, password: testCredentials.password }); if (formattedPhoneLogin.success) { console.log('โœ… Formatted phone login successful!'); console.log(` Identifier used: ${testCredentials.formattedPhone}`); console.log(` Client Name: ${formattedPhoneLogin.client.name}`); } else { console.log('โŒ Formatted phone login failed:', formattedPhoneLogin.message); } } catch (error) { console.log('โŒ Formatted phone login error:', error.message); } console.log(''); // ========================================== // PART 2: MULTI-PROVIDER CLIENT HANDLING // ========================================== console.log('๐Ÿข PART 2: Multi-Provider Client Authentication'); console.log('โ•'.repeat(60)); // Simulate multi-provider login response console.log('๐Ÿ”„ Example: Handling Multi-Provider Clients'); console.log('-'.repeat(40)); const handleMultiProviderAuth = async (identifier, password) => { try { // Step 1: Initial login attempt const loginResult = await dispatch9.loginClient({ identifier, password }); if (loginResult.success && loginResult.tokens) { // Direct login success (single provider) console.log('โœ… Single provider login successful'); return { success: true, client: loginResult.client }; } else if (loginResult.requiresProviderSelection) { // Multiple providers available console.log('๐Ÿ”„ Multiple providers detected:'); loginResult.providers.forEach((provider, index) => { console.log(` ${index + 1}. Database: ${provider.database}`); console.log(` Provider ID: ${provider.providerId}`); console.log(` Registered: ${new Date(provider.registeredAt).toLocaleDateString()}`); console.log(` Permissions: ${Object.keys(provider.permissions).length} granted`); }); // Step 2: Select first provider (in real app, user would choose) console.log('\n๐Ÿ“Œ Selecting first provider...'); const selectedProvider = loginResult.providers[0]; const providerResult = await dispatch9.selectClientProvider({ clientId: loginResult.clientId, providerId: selectedProvider.providerId }); if (providerResult.success) { console.log('โœ… Provider selection successful!'); console.log(` Connected to: ${providerResult.client.selectedProvider.database}`); console.log(` Provider ID: ${providerResult.client.selectedProvider.providerId}`); return { success: true, client: providerResult.client }; } else { console.log('โŒ Provider selection failed:', providerResult.message); return { success: false, message: providerResult.message }; } } else { console.log('โŒ Login failed:', loginResult.message); return { success: false, message: loginResult.message }; } } catch (error) { console.log('โŒ Multi-provider auth error:', error.message); return { success: false, message: error.message }; } }; // Test multi-provider authentication const multiProviderResult = await handleMultiProviderAuth( testCredentials.email, testCredentials.password ); if (multiProviderResult.success) { console.log('๐ŸŽ‰ Multi-provider authentication completed successfully!\n'); } else { console.log('โ„น๏ธ Multi-provider test completed (may not have multiple providers)\n'); } // ========================================== // PART 3: FIRST-TIME LOGIN OTP VERIFICATION // ========================================== console.log('๐Ÿ“ฒ PART 3: First-Time Login OTP Verification'); console.log('โ•'.repeat(60)); const handleFirstTimeLogin = async (identifier, password, otpCode) => { try { // Step 1: Initial login attempt console.log('๐Ÿ” Attempting first-time login...'); const loginResult = await dispatch9.loginClient({ identifier, password }); if (loginResult.success && loginResult.tokens) { // Login successful without OTP console.log('โœ… Login successful (no OTP required)'); return { success: true, client: loginResult.client }; } else if (loginResult.requiresOTP) { // OTP verification required console.log('๐Ÿ“ง First-time login detected!'); console.log(` Client ID: ${loginResult.clientId}`); console.log(` OTP sent to email: ${loginResult.message}`); if (otpCode) { // Step 2: Verify OTP console.log('๐Ÿ”ข Verifying OTP code...'); const otpResult = await dispatch9.verifyClientLoginOTP({ clientId: loginResult.clientId, otp: otpCode }); if (otpResult.success) { console.log('โœ… OTP verification successful!'); console.log(` Welcome ${otpResult.client.name}`); console.log(' ๐Ÿ”‘ Authentication tokens received'); return { success: true, client: otpResult.client }; } else { console.log('โŒ OTP verification failed:', otpResult.message); return { success: false, message: otpResult.message }; } } else { console.log('โ„น๏ธ OTP code needed for verification'); return { success: false, requiresOTP: true, clientId: loginResult.clientId }; } } else { console.log('โŒ Login failed:', loginResult.message); return { success: false, message: loginResult.message }; } } catch (error) { console.log('โŒ First-time login error:', error.message); return { success: false, message: error.message }; } }; // Test first-time login (without actual OTP) console.log('๐Ÿงช Example: First-Time Login Flow'); console.log('-'.repeat(40)); const firstTimeResult = await handleFirstTimeLogin( testCredentials.email, testCredentials.password, null // No OTP provided in example ); if (firstTimeResult.requiresOTP) { console.log('โ„น๏ธ First-time login would require OTP verification'); console.log(' In a real application:'); console.log(' 1. User receives OTP via email'); console.log(' 2. User enters OTP in your app'); console.log(' 3. Call verifyClientLoginOTP with the code'); // Example of OTP verification (with example code) console.log('\n๐Ÿ”ข Example OTP verification:'); try { await dispatch9.verifyClientLoginOTP({ clientId: firstTimeResult.clientId, otp: '123456' // Example OTP }); } catch (error) { console.log(' Expected error (invalid OTP):', error.message); } } console.log(''); // ========================================== // PART 4: CLIENT PROFILE MANAGEMENT // ========================================== console.log('๐Ÿ‘ค PART 4: Client Profile Management'); console.log('โ•'.repeat(60)); console.log('๐Ÿ“‹ Example: Getting Client Profile'); console.log('-'.repeat(40)); try { // Note: This requires authentication tokens const profile = await dispatch9.getClientProfile(); console.log('โœ… Profile retrieved successfully:'); console.log(` ID: ${profile.id}`); console.log(` Name: ${profile.name}`); console.log(` Email: ${profile.email}`); console.log(` Phone: ${profile.phone || 'Not provided'}`); console.log(` Business: ${profile.businessName || 'Not specified'}`); console.log(` Status: ${profile.status}`); console.log(` Registration: ${profile.registrationStatus}`); console.log(` First Login: ${profile.isFirstLogin ? 'Yes' : 'No'}`); console.log(` Last Login: ${profile.lastLogin || 'Never'}`); if (profile.providers && profile.providers.length > 0) { console.log('\n๐Ÿข Available Providers:'); profile.providers.forEach((provider, index) => { console.log(` ${index + 1}. Database: ${provider.database}`); console.log(` Status: ${provider.status}`); }); } } catch (error) { console.log('โ„น๏ธ Profile access requires authentication:', error.message); console.log(' In a real application, ensure client is logged in first'); } console.log(''); // ========================================== // PART 5: VALIDATION AND ERROR HANDLING // ========================================== console.log('๐Ÿ›ก๏ธ PART 5: Validation and Error Handling'); console.log('โ•'.repeat(60)); console.log('โŒ Testing Input Validation:'); console.log('-'.repeat(40)); // Test various validation scenarios const validationTests = [ { name: 'Empty credentials', test: () => dispatch9.loginClient(), expectedError: 'credentials object is required' }, { name: 'Missing identifier', test: () => dispatch9.loginClient({ password: 'test' }), expectedError: 'identifier is required' }, { name: 'Invalid email format', test: () => dispatch9.loginClient({ identifier: 'invalid-email', password: 'test' }), expectedError: 'identifier must be a valid email address or phone number' }, { name: 'Invalid phone format', test: () => dispatch9.loginClient({ identifier: '123', password: 'test' }), expectedError: 'identifier must be a valid email address or phone number' }, { name: 'Missing password', test: () => dispatch9.loginClient({ identifier: 'test@example.com' }), expectedError: 'password is required' }, { name: 'Invalid OTP format', test: () => dispatch9.verifyClientLoginOTP({ clientId: '507f1f77bcf86cd799439011', otp: '12345' }), expectedError: 'otp must be a 6-digit number' } ]; for (const validation of validationTests) { try { await validation.test(); console.log(` โŒ ${validation.name}: Validation failed to catch error`); } catch (error) { if (error.message.includes(validation.expectedError)) { console.log(` โœ… ${validation.name}: Correctly validated`); } else { console.log(` โš ๏ธ ${validation.name}: Unexpected error - ${error.message}`); } } } console.log(''); // ========================================== // PART 6: AUTHENTICATION UTILITIES // ========================================== console.log('๐Ÿ”ง PART 6: Authentication Utility Functions'); console.log('โ•'.repeat(60)); // Utility function to detect identifier type const detectIdentifierType = (identifier) => { if (!identifier) return 'invalid'; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const phoneRegex = /^\+?[1-9]\d{1,14}$/; const cleanPhone = identifier.replace(/[\s\-\(\)]/g, ''); if (emailRegex.test(identifier)) return 'email'; if (phoneRegex.test(cleanPhone)) return 'phone'; return 'invalid'; }; // Test identifier detection const testIdentifiers = [ 'user@example.com', '+1234567890', '+1 (234) 567-8900', '1234567890', 'invalid-identifier', '' ]; console.log('๐Ÿ” Identifier Type Detection:'); console.log('-'.repeat(40)); testIdentifiers.forEach(identifier => { const type = detectIdentifierType(identifier); console.log(` "${identifier}" โ†’ ${type}`); }); console.log(''); // Authentication state helper const createAuthHelper = () => { let currentClient = null; let authTokens = null; return { login: async (identifier, password) => { try { const result = await dispatch9.loginClient({ identifier, password }); if (result.success && result.tokens) { currentClient = result.client; authTokens = result.tokens; return { success: true, client: result.client }; } return { success: false, message: result.message }; } catch (error) { return { success: false, message: error.message }; } }, logout: () => { currentClient = null; authTokens = null; console.log('๐Ÿšช Client logged out'); }, isAuthenticated: () => { return !!(currentClient && authTokens); }, getCurrentClient: () => currentClient, getTokens: () => authTokens }; }; console.log('๐Ÿ” Authentication Helper Example:'); console.log('-'.repeat(40)); const authHelper = createAuthHelper(); console.log(` Is Authenticated: ${authHelper.isAuthenticated()}`); console.log(` Current Client: ${authHelper.getCurrentClient() || 'None'}`); console.log(` Tokens: ${authHelper.getTokens() || 'None'}`); // Simulate login console.log(' Simulating login...'); const authResult = await authHelper.login(testCredentials.email, testCredentials.password); console.log(` Login Result: ${authResult.success ? 'Success' : authResult.message}`); authHelper.logout(); console.log(''); // ========================================== // SUMMARY AND BEST PRACTICES // ========================================== console.log('๐Ÿ“Š SUMMARY AND BEST PRACTICES'); console.log('โ•'.repeat(60)); console.log('๐ŸŽฏ Key Features Demonstrated:'); console.log(' โœ“ Email address login'); console.log(' โœ“ Phone number login (various formats)'); console.log(' โœ“ Multi-provider client handling'); console.log(' โœ“ First-time login OTP verification'); console.log(' โœ“ Client profile management'); console.log(' โœ“ Comprehensive input validation'); console.log(' โœ“ Error handling and edge cases'); console.log('\n๐Ÿ’ก Best Practices:'); console.log(' โ€ข Always validate user input before API calls'); console.log(' โ€ข Handle all authentication scenarios (OTP, multi-provider)'); console.log(' โ€ข Implement secure token storage in production'); console.log(' โ€ข Provide clear feedback for authentication states'); console.log(' โ€ข Support both email and phone login for flexibility'); console.log(' โ€ข Implement proper error handling and user messaging'); console.log('\n๐Ÿ”’ Security Considerations:'); console.log(' โ€ข Store tokens securely (encrypted storage)'); console.log(' โ€ข Implement token refresh mechanisms'); console.log(' โ€ข Use HTTPS in production environments'); console.log(' โ€ข Validate OTP codes server-side'); console.log(' โ€ข Implement rate limiting for login attempts'); console.log(' โ€ข Log authentication events for security monitoring'); console.log('\n๐ŸŽ‰ Client Authentication Example Completed Successfully!'); } catch (error) { console.error('โŒ Error in client authentication example:', error.message); if (error.message.includes('ECONNREFUSED') || error.message.includes('fetch')) { console.error('\n๐Ÿ”ง Connection Issue:'); console.error(' โ€ข Check that the backend server is running'); console.error(' โ€ข Verify the DISPATCH9_BASE_URL environment variable'); console.error(' โ€ข Ensure the client-auth endpoints are available'); } else if (error.message.includes('credentials')) { console.error('\n๐Ÿ”ง Credentials Issue:'); console.error(' โ€ข Replace example credentials with real test accounts'); console.error(' โ€ข Ensure test clients exist in the system'); console.error(' โ€ข Verify client registration is complete'); } process.exit(1); } } // Run the example if (require.main === module) { clientAuthenticationExample(); } module.exports = clientAuthenticationExample;