@onairos/react-native 3.0.72 โ†’ 3.0.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/lib/commonjs/components/Onairos.js.map +1 -1
  2. package/lib/commonjs/index.js +23 -504
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/services/apiKeyService.js +93 -14
  5. package/lib/commonjs/services/apiKeyService.js.map +1 -1
  6. package/lib/commonjs/services/platformAuthService.js +224 -9
  7. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  8. package/lib/commonjs/types/index.js +4 -0
  9. package/lib/commonjs/types.js +12 -0
  10. package/lib/commonjs/types.js.map +1 -1
  11. package/lib/commonjs/utils/programmaticFlow.js +117 -0
  12. package/lib/commonjs/utils/programmaticFlow.js.map +1 -0
  13. package/lib/module/components/Onairos.js.map +1 -1
  14. package/lib/module/index.js +17 -64
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/module/services/apiKeyService.js +90 -11
  17. package/lib/module/services/apiKeyService.js.map +1 -1
  18. package/lib/module/services/platformAuthService.js +218 -8
  19. package/lib/module/services/platformAuthService.js.map +1 -1
  20. package/lib/module/types/index.js +1 -1
  21. package/lib/module/types.js +8 -0
  22. package/lib/module/types.js.map +1 -1
  23. package/lib/module/utils/programmaticFlow.js +111 -0
  24. package/lib/module/utils/programmaticFlow.js.map +1 -0
  25. package/lib/typescript/components/Onairos.d.ts +1 -29
  26. package/lib/typescript/components/Onairos.d.ts.map +1 -1
  27. package/lib/typescript/index.d.ts +10 -40
  28. package/lib/typescript/index.d.ts.map +1 -1
  29. package/lib/typescript/services/apiKeyService.d.ts +18 -0
  30. package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
  31. package/lib/typescript/services/platformAuthService.d.ts +26 -0
  32. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  33. package/lib/typescript/types/index.d.ts +144 -78
  34. package/lib/typescript/types/index.d.ts.map +1 -1
  35. package/lib/typescript/types.d.ts +56 -0
  36. package/lib/typescript/types.d.ts.map +1 -1
  37. package/lib/typescript/utils/programmaticFlow.d.ts +23 -0
  38. package/lib/typescript/utils/programmaticFlow.d.ts.map +1 -0
  39. package/package.json +1 -1
  40. package/src/components/Onairos.tsx +1 -30
  41. package/src/index.ts +23 -132
  42. package/src/services/apiKeyService.ts +84 -8
  43. package/src/services/platformAuthService.ts +260 -4
  44. package/src/types/index.d.ts +110 -0
  45. package/src/types/index.ts +148 -74
  46. package/src/types.ts +59 -0
  47. package/src/utils/programmaticFlow.ts +113 -0
package/src/index.ts CHANGED
@@ -1,156 +1,47 @@
1
1
  /**
2
2
  * Onairos React Native SDK
3
3
  * A React Native implementation for Onairos personalized data integration
4
+ *
5
+ * PUBLIC API - Only expose what developers need
4
6
  */
5
7
 
6
- // Export types first to avoid circular dependencies
8
+ // Essential Types Only
7
9
  export type {
8
10
  OnairosButtonProps,
11
+ OnairosProps,
9
12
  DataTier,
10
- UniversalOnboardingProps,
11
- ConnectionStatus,
12
- PlatformListProps,
13
- PinInputProps,
14
- TrainingModalProps,
15
- OAuthWebViewProps,
16
- PlatformConfig,
17
- PlatformAuthConfig,
18
- ApiResponse,
19
- CredentialsResult,
20
- OverlayProps,
21
- BiometricOptions,
22
- PinRequirements,
23
13
  OnairosConfig,
24
- ApiKeyConfig,
25
- ApiKeyValidationResult,
26
14
  TestModeOptions,
27
- DataRequest,
28
15
  } from './types';
29
16
 
30
- export type { EmailVerificationModalProps } from './components/EmailVerificationModal';
31
-
32
- export type { StorageOptions, OnairosCredentials } from './utils/secureStorage';
33
- export type { OAuthConfig } from './services/oauthService';
34
- export type { ApiErrorType, ApiError } from './utils/onairosApi';
35
-
36
- // Export constants
37
- export { COLORS, PLATFORMS, API_ENDPOINTS, STORAGE_KEYS, PIN_REQUIREMENTS, DEEP_LINK_CONFIG } from './constants';
17
+ // Core Components
18
+ export { OnairosButton } from './components/OnairosButton';
19
+ export { Onairos } from './components/Onairos';
38
20
 
39
- // Export utilities
21
+ // SDK Initialization
40
22
  export {
41
- storeCredentials,
42
- getCredentials,
43
- hasCredentials,
44
- deleteCredentials,
45
- updateCredentials,
46
- generateDeviceUsername,
47
- verifyCredentials,
48
- } from './utils/secureStorage';
49
-
50
- export {
51
- validateCredentials,
52
- createAccount,
53
- authenticate,
54
- refreshToken,
55
- getPlatformData,
56
- getUserProfile,
57
- updatePlatformConnections,
58
- } from './utils/onairosApi';
59
-
60
- export {
61
- rsaEncrypt,
62
- sha256,
63
- base64ToBuffer,
64
- } from './utils/crypto';
65
-
66
- export {
67
- logDebug,
68
- logError,
69
- isDebugMode,
70
- } from './utils/debugHelper';
71
-
72
- // Export services
73
- export {
74
- connectPlatform,
75
- initializeOAuthService,
76
- cleanupOAuthService,
77
- storePlatformConnection,
78
- } from './services/oauthService';
23
+ initializeApiKey,
24
+ ADMIN_API_KEY,
25
+ } from './services/apiKeyService';
79
26
 
27
+ // Authentication & PIN Management
80
28
  export {
81
- updateGoogleClientIds,
82
- refreshYouTubeTokens,
83
- hasNativeSDK,
84
- testApiConnectivity,
29
+ storePinAfterBiometric,
30
+ getStoredJwtToken,
31
+ clearStoredTokens,
85
32
  requestEmailVerification,
86
33
  verifyEmailCode,
87
34
  checkEmailVerificationStatus,
88
- disconnectPlatform,
89
- initiateOAuth,
90
35
  } from './services/platformAuthService';
91
36
 
92
- // API Key Service - Initialize SDK with API key
93
- export {
94
- initializeApiKey,
95
- validateApiKey,
96
- getApiConfig,
97
- getApiKey,
98
- isApiKeyInitialized,
99
- resetApiKeyService,
100
- getAuthHeaders,
101
- makeAuthenticatedRequest,
102
- } from './services/apiKeyService';
103
-
104
- // Export API and Services
105
- export { onairosApi } from './api';
106
- export { OAuthService } from './services/oauthService';
107
- export * from './utils/secureStorage';
108
- export * from './utils/encryption';
109
-
110
- // Export hooks
111
- export { useCredentials } from './hooks/useCredentials';
112
- export { useConnections } from './hooks/useConnections';
113
-
114
- // Export Portal components
115
- export { Portal, PortalHost } from './utils/Portal';
116
-
117
- // Screen Components
118
- export { ConnectorScreen } from './components/screens/ConnectorScreen';
119
- export { PinCreationScreen } from './components/screens/PinCreationScreen';
120
- export { LoadingScreen } from './components/screens/LoadingScreen';
121
-
122
- // Onboarding Components
123
- export { OAuthWebView } from './components/onboarding/OAuthWebView';
124
- export { PlatformConnector } from './components/onboarding/PlatformConnector';
125
- export { OnboardingHeader } from './components/onboarding/OnboardingHeader';
37
+ // Programmatic Function (for custom buttons)
38
+ export { executeOnairosFlow } from './utils/programmaticFlow';
126
39
 
127
- // Core Components - Export these last to avoid circular dependencies
128
- export { PlatformList } from './components/PlatformList';
129
- export { PinInput } from './components/PinInput';
130
- export { TrainingModal } from './components/TrainingModal';
131
- export { EmailVerificationModal } from './components/EmailVerificationModal';
132
- export { DataRequestScreen } from './components/DataRequestScreen';
133
- export { Overlay } from './components/Overlay';
134
- export { UniversalOnboarding } from './components/UniversalOnboarding';
135
- export { OnairosButton } from './components/OnairosButton';
136
- export { Onairos } from './components/Onairos';
137
-
138
- // Define the public components for default export
139
- import * as React from 'react';
140
- import { Onairos } from './components/Onairos';
40
+ // Default export for convenience
141
41
  import { OnairosButton } from './components/OnairosButton';
142
- import { Overlay } from './components/Overlay';
143
- import { UniversalOnboarding } from './components/UniversalOnboarding';
144
- import { PortalHost } from './utils/Portal';
145
-
146
- // Simple default export object - main types are available through named exports
147
- const components = {
148
- Onairos: Onairos as any,
149
- OnairosButton: OnairosButton as any,
150
- OnairosOverlay: Overlay as any,
151
- UniversalOnboarding: UniversalOnboarding as any,
152
- PortalHost: PortalHost as any,
153
- };
42
+ import { Onairos } from './components/Onairos';
154
43
 
155
- // Export the components as the default export
156
- export default components;
44
+ export default {
45
+ OnairosButton,
46
+ Onairos,
47
+ };
@@ -1,5 +1,15 @@
1
1
  import type { OnairosConfig, ApiKeyValidationResult } from '../types';
2
2
 
3
+ // Admin key for backend validation
4
+ export const ADMIN_API_KEY = 'OnairosIsAUnicorn2025';
5
+
6
+ // API key types
7
+ export enum ApiKeyType {
8
+ DEVELOPER = 'developer',
9
+ ADMIN = 'admin',
10
+ INVALID = 'invalid'
11
+ }
12
+
3
13
  /**
4
14
  * API Key Service for Onairos React Native SDK
5
15
  *
@@ -12,6 +22,12 @@ import type { OnairosConfig, ApiKeyValidationResult } from '../types';
12
22
  * 3. Cache validation results for performance
13
23
  * 4. Include API key in all authenticated requests
14
24
  * 5. Handle API key errors gracefully with developer-friendly messages
25
+ *
26
+ * Backend Integration:
27
+ * - All API requests include: Authorization: Bearer {apiKey}
28
+ * - Backend should validate API keys on each request
29
+ * - Admin key "OnairosIsAUnicorn2025" has full permissions
30
+ * - Developer keys have limited permissions based on validation response
15
31
  */
16
32
 
17
33
  // Global configuration state
@@ -89,6 +105,33 @@ export const initializeApiKey = async (config: OnairosConfig): Promise<void> =>
89
105
  }
90
106
  };
91
107
 
108
+ /**
109
+ * Determine API key type
110
+ * @param apiKey The API key to check
111
+ * @returns The type of API key
112
+ */
113
+ export const getApiKeyType = (apiKey: string): ApiKeyType => {
114
+ if (apiKey === ADMIN_API_KEY) {
115
+ return ApiKeyType.ADMIN;
116
+ }
117
+
118
+ // Developer keys should be at least 32 characters and start with specific prefix
119
+ if (apiKey.length >= 32 && (apiKey.startsWith('dev_') || apiKey.startsWith('pk_'))) {
120
+ return ApiKeyType.DEVELOPER;
121
+ }
122
+
123
+ return ApiKeyType.INVALID;
124
+ };
125
+
126
+ /**
127
+ * Check if API key is admin key
128
+ * @param apiKey The API key to check
129
+ * @returns True if admin key
130
+ */
131
+ export const isAdminKey = (apiKey: string): boolean => {
132
+ return apiKey === ADMIN_API_KEY;
133
+ };
134
+
92
135
  /**
93
136
  * Validate an API key with the Onairos backend
94
137
  * @param apiKey The API key to validate
@@ -98,6 +141,30 @@ export const validateApiKey = async (apiKey: string): Promise<ApiKeyValidationRe
98
141
  try {
99
142
  console.log('๐Ÿ” Validating API key...');
100
143
 
144
+ // Check if it's an admin key
145
+ if (isAdminKey(apiKey)) {
146
+ console.log('๐Ÿ”‘ Admin key detected - granting full permissions');
147
+ return {
148
+ isValid: true,
149
+ permissions: ['*'], // Full permissions for admin
150
+ rateLimits: {
151
+ remaining: 999999,
152
+ resetTime: Date.now() + 24 * 60 * 60 * 1000 // 24 hours
153
+ },
154
+ keyType: ApiKeyType.ADMIN
155
+ };
156
+ }
157
+
158
+ // Check basic format for developer keys
159
+ const keyType = getApiKeyType(apiKey);
160
+ if (keyType === ApiKeyType.INVALID) {
161
+ return {
162
+ isValid: false,
163
+ error: 'Invalid API key format. Developer keys must be at least 32 characters and start with "dev_" or "pk_"',
164
+ keyType: ApiKeyType.INVALID
165
+ };
166
+ }
167
+
101
168
  // Check cache first
102
169
  const cached = validationCache.get(apiKey);
103
170
  if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
@@ -122,11 +189,14 @@ export const validateApiKey = async (apiKey: string): Promise<ApiKeyValidationRe
122
189
  'Content-Type': 'application/json',
123
190
  'Authorization': `Bearer ${apiKey}`,
124
191
  'User-Agent': 'OnairosReactNative/1.0',
192
+ 'X-API-Key-Type': keyType,
125
193
  },
126
194
  body: JSON.stringify({
127
195
  environment,
128
- sdk_version: '3.0.71',
196
+ sdk_version: '3.0.72',
129
197
  platform: 'react-native',
198
+ keyType,
199
+ timestamp: new Date().toISOString(),
130
200
  }),
131
201
  signal: controller.signal,
132
202
  });
@@ -140,6 +210,7 @@ export const validateApiKey = async (apiKey: string): Promise<ApiKeyValidationRe
140
210
  isValid: true,
141
211
  permissions: data.permissions || [],
142
212
  rateLimits: data.rateLimits || null,
213
+ keyType: keyType,
143
214
  };
144
215
 
145
216
  // Cache the successful result
@@ -159,6 +230,7 @@ export const validateApiKey = async (apiKey: string): Promise<ApiKeyValidationRe
159
230
  const result: ApiKeyValidationResult = {
160
231
  isValid: false,
161
232
  error: errorMessage,
233
+ keyType: keyType,
162
234
  };
163
235
 
164
236
  // Don't cache failed results
@@ -168,23 +240,23 @@ export const validateApiKey = async (apiKey: string): Promise<ApiKeyValidationRe
168
240
 
169
241
  return result;
170
242
  }
171
- } catch (fetchError) {
243
+ } catch (fetchError: any) {
172
244
  clearTimeout(timeoutId);
173
245
 
174
246
  if (fetchError.name === 'AbortError') {
175
247
  const errorMessage = 'API key validation timeout';
176
248
  console.error('โฑ๏ธ API key validation timeout');
177
- return { isValid: false, error: errorMessage };
249
+ return { isValid: false, error: errorMessage, keyType: keyType };
178
250
  }
179
251
 
180
252
  const errorMessage = `Network error during API key validation: ${fetchError.message}`;
181
253
  console.error('๐ŸŒ Network error during API key validation:', fetchError);
182
- return { isValid: false, error: errorMessage };
254
+ return { isValid: false, error: errorMessage, keyType: keyType };
183
255
  }
184
- } catch (error) {
256
+ } catch (error: any) {
185
257
  const errorMessage = `API key validation error: ${error.message}`;
186
258
  console.error('โŒ API key validation error:', error);
187
- return { isValid: false, error: errorMessage };
259
+ return { isValid: false, error: errorMessage, keyType: ApiKeyType.INVALID };
188
260
  }
189
261
  };
190
262
 
@@ -221,12 +293,16 @@ export const getAuthHeaders = (): Record<string, string> => {
221
293
  throw new Error('SDK not initialized. Call initializeApiKey() first.');
222
294
  }
223
295
 
296
+ const keyType = getApiKeyType(globalConfig.apiKey);
297
+
224
298
  return {
225
299
  'Content-Type': 'application/json',
226
300
  'Authorization': `Bearer ${globalConfig.apiKey}`,
227
- 'User-Agent': 'OnairosReactNative/3.0.71',
228
- 'X-SDK-Version': '3.0.71',
301
+ 'User-Agent': 'OnairosReactNative/3.0.72',
302
+ 'X-SDK-Version': '3.0.72',
229
303
  'X-SDK-Environment': globalConfig.environment || 'production',
304
+ 'X-API-Key-Type': keyType,
305
+ 'X-Timestamp': new Date().toISOString(),
230
306
  };
231
307
  };
232
308
 
@@ -3,6 +3,49 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
  import type { PlatformAuthConfig } from '../types';
4
4
  import { makeAuthenticatedRequest, getApiConfig } from './apiKeyService';
5
5
 
6
+ // ๐Ÿ”‘ CRITICAL: Initialize API key service for authentication
7
+ let isApiKeyInitialized = false;
8
+
9
+ /**
10
+ * Initialize the API key service with the admin key for testing
11
+ * This ensures all API requests include proper authentication headers
12
+ */
13
+ export const initializePlatformAuthService = async (): Promise<void> => {
14
+ if (isApiKeyInitialized) {
15
+ console.log('๐Ÿ”‘ API key service already initialized');
16
+ return;
17
+ }
18
+
19
+ try {
20
+ // Import the initialization function
21
+ const { initializeApiKey, ADMIN_API_KEY } = await import('./apiKeyService');
22
+
23
+ // Initialize with admin key for production email service
24
+ await initializeApiKey({
25
+ apiKey: ADMIN_API_KEY, // 'OnairosIsAUnicorn2025'
26
+ environment: 'production', // Use production for email verification
27
+ enableLogging: true,
28
+ timeout: 30000,
29
+ });
30
+
31
+ isApiKeyInitialized = true;
32
+ console.log('โœ… Platform auth service initialized with admin key (production)');
33
+ } catch (error) {
34
+ console.error('โŒ Failed to initialize platform auth service:', error);
35
+ throw error;
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Ensure API key is initialized before making authenticated requests
41
+ */
42
+ const ensureApiKeyInitialized = async (): Promise<void> => {
43
+ if (!isApiKeyInitialized) {
44
+ console.log('๐Ÿ”‘ API key not initialized, initializing now...');
45
+ await initializePlatformAuthService();
46
+ }
47
+ };
48
+
6
49
  // Configuration for each platform's authentication
7
50
  let PLATFORM_AUTH_CONFIG: Record<string, PlatformAuthConfig> = {
8
51
  instagram: {
@@ -780,6 +823,7 @@ export const requestEmailVerification = async (email: string, testMode = false):
780
823
  success: boolean;
781
824
  message?: string;
782
825
  error?: string;
826
+ requestId?: string;
783
827
  }> => {
784
828
  try {
785
829
  console.log('๐Ÿ“ง Requesting email verification for:', email);
@@ -792,17 +836,27 @@ export const requestEmailVerification = async (email: string, testMode = false):
792
836
  };
793
837
  }
794
838
 
795
- // In test mode, always return success
839
+ // In test mode, always return success with mock request ID
796
840
  if (testMode) {
797
- console.log('๐Ÿงช Test mode: Always returning success');
841
+ console.log('๐Ÿงช Test mode: Always returning success with mock request ID');
842
+ const mockRequestId = 'test-request-' + Date.now();
843
+
844
+ // Store request info for tracking
845
+ await AsyncStorage.setItem('email_verification_request_id', mockRequestId);
846
+ await AsyncStorage.setItem('email_verification_request_email', email);
847
+
798
848
  return {
799
849
  success: true,
800
850
  message: 'Email verification sent successfully (test mode)',
851
+ requestId: mockRequestId,
801
852
  };
802
853
  }
803
854
 
804
855
  // Production mode: Make real API call with API key authentication
805
856
  try {
857
+ // ๐Ÿ”‘ Ensure API key is initialized before making authenticated requests
858
+ await ensureApiKeyInitialized();
859
+
806
860
  const response = await makeAuthenticatedRequest('/email/verification', {
807
861
  method: 'POST',
808
862
  body: JSON.stringify({
@@ -816,9 +870,16 @@ export const requestEmailVerification = async (email: string, testMode = false):
816
870
 
817
871
  if (response.ok && result.success) {
818
872
  console.log('โœ… Email verification request sent');
873
+
874
+ // Store request info for tracking
875
+ const requestId = result.requestId || result.id || ('req-' + Date.now());
876
+ await AsyncStorage.setItem('email_verification_request_id', requestId);
877
+ await AsyncStorage.setItem('email_verification_request_email', email);
878
+
819
879
  return {
820
880
  success: true,
821
881
  message: result.message || 'Email verification sent successfully',
882
+ requestId: requestId,
822
883
  };
823
884
  } else {
824
885
  console.error('โŒ Email verification request failed:', result.error);
@@ -848,6 +909,7 @@ export const verifyEmailCode = async (email: string, code: string, testMode = fa
848
909
  message?: string;
849
910
  error?: string;
850
911
  existingUser?: boolean;
912
+ jwtToken?: string;
851
913
  }> => {
852
914
  try {
853
915
  console.log('๐Ÿ” Verifying email code for:', email);
@@ -868,18 +930,29 @@ export const verifyEmailCode = async (email: string, code: string, testMode = fa
868
930
  };
869
931
  }
870
932
 
871
- // In test mode, always return success
933
+ // In test mode, always return success with mock JWT token
872
934
  if (testMode) {
873
- console.log('๐Ÿงช Test mode: Always returning success');
935
+ console.log('๐Ÿงช Test mode: Always returning success with mock JWT token');
936
+ const mockToken = 'test-jwt-token-' + Date.now();
937
+
938
+ // Store mock token for API requests
939
+ await AsyncStorage.setItem('email_verification_token', mockToken);
940
+ await AsyncStorage.setItem('onairos_jwt_token', mockToken);
941
+ await AsyncStorage.setItem('email_verification_email', email);
942
+
874
943
  return {
875
944
  success: true,
876
945
  message: 'Email verification successful (test mode)',
877
946
  existingUser: false,
947
+ jwtToken: mockToken,
878
948
  };
879
949
  }
880
950
 
881
951
  // Production mode: Make real API call with API key authentication
882
952
  try {
953
+ // ๐Ÿ”‘ Ensure API key is initialized before making authenticated requests
954
+ await ensureApiKeyInitialized();
955
+
883
956
  const response = await makeAuthenticatedRequest('/email/verification', {
884
957
  method: 'POST',
885
958
  body: JSON.stringify({
@@ -894,10 +967,27 @@ export const verifyEmailCode = async (email: string, code: string, testMode = fa
894
967
 
895
968
  if (response.ok && result.success) {
896
969
  console.log('โœ… Email verification successful');
970
+
971
+ // ๐ŸŽซ CRITICAL: Store JWT token from email verification response
972
+ const jwtToken = result.token || result.jwtToken || result.jwt || result.authToken;
973
+
974
+ if (jwtToken) {
975
+ console.log('๐ŸŽซ Storing JWT token from email verification response');
976
+ await AsyncStorage.setItem('email_verification_token', jwtToken);
977
+ await AsyncStorage.setItem('onairos_jwt_token', jwtToken);
978
+ await AsyncStorage.setItem('enoch_token', jwtToken);
979
+ await AsyncStorage.setItem('auth_token', jwtToken);
980
+ await AsyncStorage.setItem('email_verification_email', email);
981
+ await AsyncStorage.setItem('token_timestamp', Date.now().toString());
982
+ } else {
983
+ console.warn('โš ๏ธ No JWT token received from email verification API');
984
+ }
985
+
897
986
  return {
898
987
  success: true,
899
988
  message: result.message || 'Email verification successful',
900
989
  existingUser: result.existingUser || false,
990
+ jwtToken: jwtToken,
901
991
  };
902
992
  } else {
903
993
  console.error('โŒ Email verification failed:', result.error);
@@ -944,6 +1034,9 @@ export const checkEmailVerificationStatus = async (email: string, testMode = fal
944
1034
 
945
1035
  // Production mode: Make real API call with API key authentication
946
1036
  try {
1037
+ // ๐Ÿ”‘ Ensure API key is initialized before making authenticated requests
1038
+ await ensureApiKeyInitialized();
1039
+
947
1040
  const response = await makeAuthenticatedRequest(`/email/verify/status/${encodeURIComponent(email)}`, {
948
1041
  method: 'GET',
949
1042
  });
@@ -1033,3 +1126,166 @@ export const disconnectPlatform = async (platform: string, username: string): Pr
1033
1126
  };
1034
1127
  }
1035
1128
  };
1129
+
1130
+ /**
1131
+ * ๐Ÿ” STORE PIN AFTER BIOMETRIC AUTHENTICATION
1132
+ * Send PIN separately to /store-pin/web endpoint after biometric Face ID verification
1133
+ */
1134
+ export const storePinAfterBiometric = async (username: string, pin: string, jwtToken?: string): Promise<{
1135
+ success: boolean;
1136
+ message?: string;
1137
+ error?: string;
1138
+ }> => {
1139
+ try {
1140
+ console.log('๐Ÿ” Storing PIN after biometric authentication for user:', username);
1141
+ console.log('๐Ÿ”‘ PIN length:', pin.length);
1142
+ console.log('๐ŸŽซ JWT token provided:', !!jwtToken);
1143
+
1144
+ if (!username || !pin) {
1145
+ return {
1146
+ success: false,
1147
+ error: 'Username and PIN are required',
1148
+ };
1149
+ }
1150
+
1151
+ if (pin.length < 4) {
1152
+ return {
1153
+ success: false,
1154
+ error: 'PIN must be at least 4 digits',
1155
+ };
1156
+ }
1157
+
1158
+ // Get JWT token from storage if not provided
1159
+ let authToken = jwtToken;
1160
+ if (!authToken) {
1161
+ authToken = await AsyncStorage.getItem('onairos_jwt_token') ||
1162
+ await AsyncStorage.getItem('enoch_token') ||
1163
+ await AsyncStorage.getItem('auth_token') ||
1164
+ await AsyncStorage.getItem('email_verification_token');
1165
+ }
1166
+
1167
+ if (!authToken) {
1168
+ console.warn('โš ๏ธ No JWT token available for PIN storage');
1169
+ return {
1170
+ success: false,
1171
+ error: 'No authentication token available',
1172
+ };
1173
+ }
1174
+
1175
+ console.log('๐Ÿ“ค Sending PIN to /store-pin/web endpoint');
1176
+
1177
+ // Make authenticated request to store PIN
1178
+ const response = await makeAuthenticatedRequest('/store-pin/web', {
1179
+ method: 'POST',
1180
+ headers: {
1181
+ 'Authorization': `Bearer ${authToken}`,
1182
+ },
1183
+ body: JSON.stringify({
1184
+ username,
1185
+ pin,
1186
+ }),
1187
+ });
1188
+
1189
+ console.log('๐Ÿ“ก PIN storage response status:', response.status);
1190
+
1191
+ if (!response.ok) {
1192
+ const errorText = await response.text();
1193
+ console.error('โŒ PIN storage failed:', errorText);
1194
+ return {
1195
+ success: false,
1196
+ error: `PIN storage failed: ${response.status} - ${errorText}`,
1197
+ };
1198
+ }
1199
+
1200
+ const result = await response.json();
1201
+ console.log('๐Ÿ“ฅ PIN storage response:', result);
1202
+
1203
+ if (result.success) {
1204
+ console.log('โœ… PIN stored successfully after biometric authentication');
1205
+
1206
+ // Store PIN locally for future use
1207
+ await AsyncStorage.setItem('user_pin_stored', 'true');
1208
+ await AsyncStorage.setItem('pin_storage_timestamp', Date.now().toString());
1209
+
1210
+ return {
1211
+ success: true,
1212
+ message: result.message || 'PIN stored successfully',
1213
+ };
1214
+ } else {
1215
+ console.error('โŒ PIN storage API returned error:', result.error);
1216
+ return {
1217
+ success: false,
1218
+ error: result.error || 'PIN storage failed',
1219
+ };
1220
+ }
1221
+ } catch (error) {
1222
+ console.error('โŒ Error storing PIN after biometric authentication:', error);
1223
+ return {
1224
+ success: false,
1225
+ error: error instanceof Error ? error.message : 'PIN storage failed',
1226
+ };
1227
+ }
1228
+ };
1229
+
1230
+ /**
1231
+ * ๐ŸŽซ GET STORED JWT TOKEN
1232
+ * Helper function to retrieve stored JWT token from email verification or other sources
1233
+ */
1234
+ export const getStoredJwtToken = async (): Promise<string | null> => {
1235
+ try {
1236
+ console.log('๐ŸŽซ Retrieving stored JWT token...');
1237
+
1238
+ // Try different storage keys in order of preference
1239
+ const tokenSources = [
1240
+ 'email_verification_token',
1241
+ 'onairos_jwt_token',
1242
+ 'enoch_token',
1243
+ 'auth_token',
1244
+ ];
1245
+
1246
+ for (const source of tokenSources) {
1247
+ const token = await AsyncStorage.getItem(source);
1248
+ if (token && token.length > 20) {
1249
+ console.log(`โœ… JWT token found in ${source}:`, token.substring(0, 20) + '...');
1250
+ return token;
1251
+ }
1252
+ }
1253
+
1254
+ console.warn('โš ๏ธ No JWT token found in storage');
1255
+ return null;
1256
+ } catch (error) {
1257
+ console.error('โŒ Error retrieving JWT token:', error);
1258
+ return null;
1259
+ }
1260
+ };
1261
+
1262
+ /**
1263
+ * ๐ŸŽซ CLEAR STORED TOKENS
1264
+ * Helper function to clear all stored tokens (useful for logout)
1265
+ */
1266
+ export const clearStoredTokens = async (): Promise<void> => {
1267
+ try {
1268
+ console.log('๐Ÿงน Clearing all stored tokens...');
1269
+
1270
+ const tokenKeys = [
1271
+ 'email_verification_token',
1272
+ 'onairos_jwt_token',
1273
+ 'enoch_token',
1274
+ 'auth_token',
1275
+ 'email_verification_email',
1276
+ 'email_verification_request_id',
1277
+ 'email_verification_request_email',
1278
+ 'token_timestamp',
1279
+ 'user_pin_stored',
1280
+ 'pin_storage_timestamp',
1281
+ ];
1282
+
1283
+ await Promise.all(
1284
+ tokenKeys.map(key => AsyncStorage.removeItem(key))
1285
+ );
1286
+
1287
+ console.log('โœ… All tokens cleared successfully');
1288
+ } catch (error) {
1289
+ console.error('โŒ Error clearing tokens:', error);
1290
+ }
1291
+ };