@onairos/react-native 3.1.16 → 3.1.17
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.
- package/README.md +404 -0
- package/lib/commonjs/assets/images/Checkbox.svg +3 -3
- package/lib/commonjs/assets/images/EnochE.svg +19 -19
- package/lib/commonjs/assets/images/Personalityprofile.svg +3 -3
- package/lib/commonjs/assets/images/Personalitytraits.svg +3 -3
- package/lib/commonjs/assets/images/Userpreferences.svg +3 -3
- package/lib/commonjs/assets/images/arrow.svg +20 -20
- package/lib/commonjs/assets/images/basicproficon.svg +43 -43
- package/lib/commonjs/assets/images/basicprofile.svg +3 -3
- package/lib/commonjs/assets/images/checkmark.svg +4 -4
- package/lib/commonjs/assets/images/contentanalysis.svg +3 -3
- package/lib/commonjs/assets/images/contenticon.svg +23 -23
- package/lib/commonjs/assets/images/personalityicon.svg +18 -18
- package/lib/commonjs/assets/images/x-close.svg +3 -3
- package/lib/commonjs/components/OnairosButton.js +290 -0
- package/lib/commonjs/components/OnairosButton.js.map +1 -0
- package/lib/commonjs/components/OnairosSignInButton.js +30 -8
- package/lib/commonjs/components/OnairosSignInButton.js.map +1 -1
- package/lib/commonjs/components/UniversalOnboarding.js +4 -4
- package/lib/commonjs/config/api.js +2 -2
- package/lib/commonjs/hooks/useConnections.js +6 -6
- package/lib/commonjs/hooks/useUserConnections.js +10 -10
- package/lib/commonjs/index.js +9 -10
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/services/apiClient.js +35 -35
- package/lib/commonjs/services/apiKeyService.js +99 -99
- package/lib/commonjs/services/authService.js +82 -82
- package/lib/commonjs/services/biometricPinService.js +10 -10
- package/lib/commonjs/services/connectedAccountsService.js +32 -32
- package/lib/commonjs/services/googleAuthService.js +15 -15
- package/lib/commonjs/services/imageCompressionService.js +15 -15
- package/lib/commonjs/services/jwtStorageService.js +59 -59
- package/lib/commonjs/services/mobileTrainingService.js +14 -14
- package/lib/commonjs/services/pinEncryptionService.js +10 -10
- package/lib/commonjs/services/pinStorageUtils.js +15 -15
- package/lib/commonjs/services/platformAuthService.js +47 -47
- package/lib/commonjs/services/storageService.js +31 -31
- package/lib/commonjs/services/trainingApiHelpers.js +33 -33
- package/lib/commonjs/services/userConnectionsService.js +24 -24
- package/lib/commonjs/utils/Portal.js +4 -4
- package/lib/commonjs/utils/api.js +24 -24
- package/lib/commonjs/utils/auth.js +18 -18
- package/lib/commonjs/utils/crypto.js +13 -13
- package/lib/commonjs/utils/encryption.js +12 -12
- package/lib/commonjs/utils/eventUtils.js +52 -52
- package/lib/commonjs/utils/programmaticFlow.js +16 -16
- package/lib/commonjs/utils/retryHelper.js +27 -27
- package/lib/module/assets/images/Checkbox.svg +3 -3
- package/lib/module/assets/images/EnochE.svg +19 -19
- package/lib/module/assets/images/Personalityprofile.svg +3 -3
- package/lib/module/assets/images/Personalitytraits.svg +3 -3
- package/lib/module/assets/images/Userpreferences.svg +3 -3
- package/lib/module/assets/images/arrow.svg +20 -20
- package/lib/module/assets/images/basicproficon.svg +43 -43
- package/lib/module/assets/images/basicprofile.svg +3 -3
- package/lib/module/assets/images/checkmark.svg +4 -4
- package/lib/module/assets/images/contentanalysis.svg +3 -3
- package/lib/module/assets/images/contenticon.svg +23 -23
- package/lib/module/assets/images/personalityicon.svg +18 -18
- package/lib/module/assets/images/x-close.svg +3 -3
- package/lib/module/components/OnairosButton.js +282 -0
- package/lib/module/components/OnairosButton.js.map +1 -0
- package/lib/module/components/OnairosSignInButton.js +30 -8
- package/lib/module/components/OnairosSignInButton.js.map +1 -1
- package/lib/module/components/UniversalOnboarding.js +4 -4
- package/lib/module/config/api.js +2 -2
- package/lib/module/hooks/useConnections.js +6 -6
- package/lib/module/hooks/useUserConnections.js +10 -10
- package/lib/module/index.js +8 -10
- package/lib/module/index.js.map +1 -1
- package/lib/module/services/apiClient.js +35 -35
- package/lib/module/services/apiKeyService.js +99 -99
- package/lib/module/services/authService.js +82 -82
- package/lib/module/services/biometricPinService.js +10 -10
- package/lib/module/services/connectedAccountsService.js +32 -32
- package/lib/module/services/googleAuthService.js +15 -15
- package/lib/module/services/imageCompressionService.js +15 -15
- package/lib/module/services/jwtStorageService.js +59 -59
- package/lib/module/services/mobileTrainingService.js +14 -14
- package/lib/module/services/pinEncryptionService.js +10 -10
- package/lib/module/services/pinStorageUtils.js +15 -15
- package/lib/module/services/platformAuthService.js +47 -47
- package/lib/module/services/storageService.js +31 -31
- package/lib/module/services/trainingApiHelpers.js +33 -33
- package/lib/module/services/userConnectionsService.js +24 -24
- package/lib/module/utils/Portal.js +4 -4
- package/lib/module/utils/api.js +24 -24
- package/lib/module/utils/auth.js +18 -18
- package/lib/module/utils/crypto.js +13 -13
- package/lib/module/utils/encryption.js +12 -12
- package/lib/module/utils/eventUtils.js +52 -52
- package/lib/module/utils/programmaticFlow.js +16 -16
- package/lib/module/utils/retryHelper.js +27 -27
- package/lib/typescript/components/OnairosButton.d.ts +37 -0
- package/lib/typescript/components/OnairosButton.d.ts.map +1 -0
- package/lib/typescript/components/OnairosSignInButton.d.ts +2 -1
- package/lib/typescript/components/OnairosSignInButton.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +3 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +163 -163
- package/src/api/index.ts +151 -151
- package/src/assets/images/Checkbox.svg +3 -3
- package/src/assets/images/EnochE.svg +19 -19
- package/src/assets/images/Personalityprofile.svg +3 -3
- package/src/assets/images/Personalitytraits.svg +3 -3
- package/src/assets/images/Userpreferences.svg +3 -3
- package/src/assets/images/arrow.svg +20 -20
- package/src/assets/images/basicproficon.svg +43 -43
- package/src/assets/images/basicprofile.svg +3 -3
- package/src/assets/images/checkmark.svg +4 -4
- package/src/assets/images/contentanalysis.svg +3 -3
- package/src/assets/images/contenticon.svg +23 -23
- package/src/assets/images/personalityicon.svg +18 -18
- package/src/assets/images/x-close.svg +3 -3
- package/src/components/BodyText.tsx +33 -33
- package/src/components/BrandMark.tsx +62 -62
- package/src/components/CodeInput.tsx +32 -32
- package/src/components/DataRequestScreen.tsx +355 -355
- package/src/components/EmailInput.tsx +31 -31
- package/src/components/EmailVerificationModal.tsx +363 -363
- package/src/components/ExistingUserDataConfirmation.tsx +506 -506
- package/src/components/GoogleButton.tsx +55 -55
- package/src/components/HeadingGroup.tsx +49 -49
- package/src/components/ModalHeader.tsx +125 -125
- package/src/components/ModalSheet.tsx +57 -57
- package/src/components/Onairos.tsx +422 -422
- package/src/components/OnairosButton.tsx +339 -0
- package/src/components/OnairosSignInButton.tsx +30 -10
- package/src/components/Overlay.tsx +506 -506
- package/src/components/PersonaImage.tsx +79 -79
- package/src/components/PersonaLoadingScreen.tsx +201 -201
- package/src/components/PersonalizationConsentScreen.tsx +410 -410
- package/src/components/PinCreationScreen.tsx +492 -492
- package/src/components/PinInput.tsx +555 -555
- package/src/components/PlatformConnectorsStep.tsx +891 -891
- package/src/components/PlatformList.tsx +144 -144
- package/src/components/PlatformToggle.tsx +226 -226
- package/src/components/PrimaryButton.tsx +213 -213
- package/src/components/SignInMatchAnimation.tsx +225 -225
- package/src/components/SignInStep.tsx +217 -217
- package/src/components/TrainingModal.tsx +1047 -1047
- package/src/components/UniversalOnboarding.tsx +2887 -2887
- package/src/components/VerificationStep.tsx +198 -198
- package/src/components/WelcomeScreen.tsx +473 -473
- package/src/components/icons/Basicproficon.tsx +30 -30
- package/src/components/icons/Basicprofile.tsx +17 -17
- package/src/components/icons/Checkbox.tsx +17 -17
- package/src/components/icons/Checkmark.tsx +24 -24
- package/src/components/icons/Contentanalysis.tsx +17 -17
- package/src/components/icons/Contenticon.tsx +30 -30
- package/src/components/icons/EnochE.tsx +39 -39
- package/src/components/icons/Personalityicon.tsx +22 -22
- package/src/components/icons/Personalityprofile.tsx +17 -17
- package/src/components/icons/Personalitytraits.tsx +17 -17
- package/src/components/icons/Userpreferences.tsx +17 -17
- package/src/components/icons/index.ts +12 -12
- package/src/components/onboarding/OAuthWebView.tsx +232 -232
- package/src/config/api.ts +25 -25
- package/src/context/AuthContext.tsx +393 -393
- package/src/hooks/useConnectedAccounts.ts +138 -138
- package/src/hooks/useConnections.ts +161 -161
- package/src/hooks/useCredentials.ts +174 -174
- package/src/hooks/useUserConnections.ts +165 -165
- package/src/index.js +14 -0
- package/src/index.ts +94 -96
- package/src/services/apiClient.ts +336 -336
- package/src/services/apiKeyService.ts +919 -919
- package/src/services/authService.ts +1008 -1008
- package/src/services/biometricPinService.ts +192 -192
- package/src/services/connectedAccountsService.ts +289 -289
- package/src/services/googleAuthService.ts +279 -279
- package/src/services/imageCompressionService.ts +302 -302
- package/src/services/jwtStorageService.ts +256 -256
- package/src/services/mobileTrainingService.ts +203 -203
- package/src/services/pinEncryptionService.ts +75 -75
- package/src/services/pinStorageUtils.ts +96 -96
- package/src/services/platformAuthService.ts +1346 -1346
- package/src/services/storageService.ts +451 -451
- package/src/services/trainingApiHelpers.ts +66 -66
- package/src/services/userConnectionsService.ts +556 -556
- package/src/services/youtubeMigrationService.ts +453 -453
- package/src/theme/index.ts +239 -239
- package/src/types/ambient.d.ts +28 -28
- package/src/types/index.ts +265 -265
- package/src/types/node-fix.d.ts +18 -18
- package/src/types/node-override.d.ts +23 -23
- package/src/types/opacity.d.ts +15 -15
- package/src/types/types.d.ts +17 -17
- package/src/utils/Portal.tsx +82 -82
- package/src/utils/api.js +111 -111
- package/src/utils/auth.js +103 -103
- package/src/utils/crypto.js +59 -59
- package/src/utils/encryption.ts +68 -68
- package/src/utils/eventUtils.ts +302 -302
- package/src/utils/haptics.ts +58 -58
- package/src/utils/imagePreloader.ts +2 -2
- package/src/utils/programmaticFlow.ts +112 -112
- package/src/utils/retryHelper.ts +274 -274
|
@@ -1,337 +1,337 @@
|
|
|
1
|
-
import { getJWTForRoute, TokenType, getTokenTypeForRoute, clearJWT, getAllTokens } from './jwtStorageService';
|
|
2
|
-
import { API_CONFIG } from '../config/api';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Centralized API Client with Route-Based JWT Token Management
|
|
6
|
-
* CORRECTED APPROACH: Uses different JWT tokens for different route families
|
|
7
|
-
*
|
|
8
|
-
* TOKEN ROUTING:
|
|
9
|
-
* - Enoch JWT: /enoch/*, /api/auth/*, /mobile-training/enoch
|
|
10
|
-
* - Onairos JWT: /youtube/*, /gmail/*, social connections
|
|
11
|
-
* - Auth Token: Context-specific auth flows
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
interface ApiRequestOptions {
|
|
15
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
16
|
-
body?: any;
|
|
17
|
-
headers?: Record<string, string>;
|
|
18
|
-
requiresAuth?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ApiResponse<T = any> {
|
|
22
|
-
success: boolean;
|
|
23
|
-
data?: T;
|
|
24
|
-
error?: string;
|
|
25
|
-
status: number;
|
|
26
|
-
tokenType?: TokenType;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Global request counter for tracking in-flight requests
|
|
31
|
-
*/
|
|
32
|
-
let currentRequestId = 0;
|
|
33
|
-
const activeRequests = new Map<number, AbortController>();
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Route-specific authentication triggers
|
|
37
|
-
*/
|
|
38
|
-
interface AuthTriggers {
|
|
39
|
-
triggerEnochReAuth?: () => Promise<void>;
|
|
40
|
-
triggerOnairosReAuth?: () => Promise<void>;
|
|
41
|
-
triggerAuthTokenRefresh?: () => Promise<void>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Create API client with route-based token management
|
|
46
|
-
*/
|
|
47
|
-
class ApiClient {
|
|
48
|
-
private baseUrl: string;
|
|
49
|
-
private authTriggers: AuthTriggers;
|
|
50
|
-
|
|
51
|
-
constructor(baseUrl: string = API_CONFIG.BASE_URL, authTriggers: AuthTriggers = {}) {
|
|
52
|
-
this.baseUrl = baseUrl;
|
|
53
|
-
this.authTriggers = authTriggers;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Make authenticated API request with route-based token selection
|
|
58
|
-
* CRITICAL: Uses the correct token type based on route
|
|
59
|
-
*/
|
|
60
|
-
async request<T = any>(
|
|
61
|
-
endpoint: string,
|
|
62
|
-
options: ApiRequestOptions = {}
|
|
63
|
-
): Promise<ApiResponse<T>> {
|
|
64
|
-
const requestId = ++currentRequestId;
|
|
65
|
-
const abortController = new AbortController();
|
|
66
|
-
activeRequests.set(requestId, abortController);
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
console.log(`🔗 [API Client] Starting request ${requestId} to: ${endpoint}`);
|
|
70
|
-
|
|
71
|
-
// CRITICAL FIX: Determine which token type to use based on route
|
|
72
|
-
const tokenType = getTokenTypeForRoute(endpoint);
|
|
73
|
-
console.log(`🎯 [API Client] Route ${endpoint} requires ${tokenType} token`);
|
|
74
|
-
|
|
75
|
-
// Get route-specific headers
|
|
76
|
-
const headers = await this.getRequestHeaders(endpoint, options.requiresAuth !== false);
|
|
77
|
-
|
|
78
|
-
const fetchOptions: RequestInit = {
|
|
79
|
-
method: options.method || 'GET',
|
|
80
|
-
headers: {
|
|
81
|
-
'Content-Type': 'application/json',
|
|
82
|
-
'User-Agent': 'OnairosSDK/1.0.0',
|
|
83
|
-
...headers,
|
|
84
|
-
...options.headers,
|
|
85
|
-
},
|
|
86
|
-
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
87
|
-
signal: abortController.signal,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
console.log(`🔐 [API Client] Request ${requestId} using ${tokenType} token`);
|
|
91
|
-
|
|
92
|
-
const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
|
|
93
|
-
|
|
94
|
-
console.log(`📡 [API Client] Response ${requestId} status:`, response.status);
|
|
95
|
-
|
|
96
|
-
// CRITICAL FIX: Route-specific 401 handler
|
|
97
|
-
if (response.status === 401) {
|
|
98
|
-
return await this.handle401Error(requestId, endpoint, tokenType, options);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const result = await response.json();
|
|
102
|
-
|
|
103
|
-
activeRequests.delete(requestId);
|
|
104
|
-
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
return {
|
|
107
|
-
success: false,
|
|
108
|
-
error: result.message || `Request failed with status ${response.status}`,
|
|
109
|
-
status: response.status,
|
|
110
|
-
tokenType,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
success: true,
|
|
116
|
-
data: result,
|
|
117
|
-
status: response.status,
|
|
118
|
-
tokenType,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
} catch (error) {
|
|
122
|
-
activeRequests.delete(requestId);
|
|
123
|
-
|
|
124
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
125
|
-
console.log(`🚫 [API Client] Request ${requestId} was cancelled`);
|
|
126
|
-
return {
|
|
127
|
-
success: false,
|
|
128
|
-
error: 'Request cancelled',
|
|
129
|
-
status: 0,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.error(`❌ [API Client] Request ${requestId} failed:`, error);
|
|
134
|
-
return {
|
|
135
|
-
success: false,
|
|
136
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
137
|
-
status: 0,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Get request headers with route-appropriate token
|
|
144
|
-
* CRITICAL: Selects correct token based on route
|
|
145
|
-
*/
|
|
146
|
-
private async getRequestHeaders(endpoint: string, requiresAuth: boolean): Promise<Record<string, string>> {
|
|
147
|
-
if (!requiresAuth) {
|
|
148
|
-
return {};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// CRITICAL FIX: Get token specifically for this route
|
|
152
|
-
const token = await getJWTForRoute(endpoint);
|
|
153
|
-
|
|
154
|
-
if (!token) {
|
|
155
|
-
const tokenType = getTokenTypeForRoute(endpoint);
|
|
156
|
-
throw new Error(`No ${tokenType} token available for route ${endpoint}. Please authenticate first.`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
'Authorization': token,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Handle 401 errors with route-specific re-authentication
|
|
166
|
-
* CRITICAL FIX: Different auth flows for different token types
|
|
167
|
-
*/
|
|
168
|
-
private async handle401Error<T>(
|
|
169
|
-
requestId: number,
|
|
170
|
-
endpoint: string,
|
|
171
|
-
tokenType: TokenType,
|
|
172
|
-
options: ApiRequestOptions
|
|
173
|
-
): Promise<ApiResponse<T>> {
|
|
174
|
-
console.error(`🚫 [API Client] Request ${requestId} received 401 for ${tokenType} token on route: ${endpoint}`);
|
|
175
|
-
|
|
176
|
-
// CRITICAL FIX: Clear only the specific token type that failed
|
|
177
|
-
console.log(`🔄 [API Client] Clearing ${tokenType} token due to 401 error`);
|
|
178
|
-
await clearJWT(tokenType);
|
|
179
|
-
|
|
180
|
-
// Cancel requests using the same token type
|
|
181
|
-
this.cancelRequestsForTokenType(tokenType, requestId);
|
|
182
|
-
|
|
183
|
-
// CRITICAL FIX: Route-specific re-authentication
|
|
184
|
-
await this.triggerReAuthForTokenType(tokenType, endpoint);
|
|
185
|
-
|
|
186
|
-
console.log(`🔑 [API Client] ${tokenType} token cleared, user needs to re-authenticate`);
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
success: false,
|
|
190
|
-
error: `${tokenType} authentication expired for ${endpoint}. Please re-authenticate.`,
|
|
191
|
-
status: 401,
|
|
192
|
-
tokenType,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Trigger appropriate re-authentication based on token type
|
|
198
|
-
*/
|
|
199
|
-
private async triggerReAuthForTokenType(tokenType: TokenType, endpoint: string): Promise<void> {
|
|
200
|
-
console.log(`🔄 [API Client] Triggering re-auth for ${tokenType} token (endpoint: ${endpoint})`);
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
switch (tokenType) {
|
|
204
|
-
case TokenType.ENOCH:
|
|
205
|
-
if (this.authTriggers.triggerEnochReAuth) {
|
|
206
|
-
console.log('📧 [API Client] Triggering Enoch email re-verification');
|
|
207
|
-
await this.authTriggers.triggerEnochReAuth();
|
|
208
|
-
} else {
|
|
209
|
-
console.warn('⚠️ [API Client] No Enoch re-auth trigger configured');
|
|
210
|
-
}
|
|
211
|
-
break;
|
|
212
|
-
|
|
213
|
-
case TokenType.ONAIROS:
|
|
214
|
-
if (this.authTriggers.triggerOnairosReAuth) {
|
|
215
|
-
console.log('🔑 [API Client] Triggering Onairos re-authentication');
|
|
216
|
-
await this.authTriggers.triggerOnairosReAuth();
|
|
217
|
-
} else {
|
|
218
|
-
console.warn('⚠️ [API Client] No Onairos re-auth trigger configured');
|
|
219
|
-
}
|
|
220
|
-
break;
|
|
221
|
-
|
|
222
|
-
case TokenType.AUTH:
|
|
223
|
-
if (this.authTriggers.triggerAuthTokenRefresh) {
|
|
224
|
-
console.log('🔄 [API Client] Triggering auth token refresh');
|
|
225
|
-
await this.authTriggers.triggerAuthTokenRefresh();
|
|
226
|
-
} else {
|
|
227
|
-
console.warn('⚠️ [API Client] No auth token refresh trigger configured');
|
|
228
|
-
}
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.error(`❌ [API Client] Failed to trigger re-auth for ${tokenType}:`, error);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Cancel requests using a specific token type
|
|
238
|
-
*/
|
|
239
|
-
private cancelRequestsForTokenType(tokenType: TokenType, excludeRequestId?: number): void {
|
|
240
|
-
let cancelledCount = 0;
|
|
241
|
-
|
|
242
|
-
// Note: In a full implementation, we'd track which requests use which tokens
|
|
243
|
-
// For now, we cancel all other requests as a safety measure
|
|
244
|
-
for (const [requestId, controller] of activeRequests.entries()) {
|
|
245
|
-
if (requestId !== excludeRequestId) {
|
|
246
|
-
controller.abort();
|
|
247
|
-
activeRequests.delete(requestId);
|
|
248
|
-
cancelledCount++;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (cancelledCount > 0) {
|
|
253
|
-
console.log(`🚫 [API Client] Cancelled ${cancelledCount} requests due to ${tokenType} token failure`);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Replace token for specific type and cancel related requests
|
|
259
|
-
*/
|
|
260
|
-
async replaceTokenForType(tokenType: TokenType, newToken: string): Promise<boolean> {
|
|
261
|
-
console.log(`🔄 [API Client] Replacing ${tokenType} token`);
|
|
262
|
-
|
|
263
|
-
// Cancel all in-flight requests using this token type
|
|
264
|
-
this.cancelRequestsForTokenType(tokenType);
|
|
265
|
-
|
|
266
|
-
// Replace token using storage service
|
|
267
|
-
const { replaceJWTAfterVerification } = await import('./jwtStorageService');
|
|
268
|
-
const replaced = await replaceJWTAfterVerification(tokenType, newToken);
|
|
269
|
-
|
|
270
|
-
if (replaced) {
|
|
271
|
-
console.log(`✅ [API Client] ${tokenType} token replaced and related requests cancelled`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return replaced;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Debug: Get all current tokens
|
|
279
|
-
*/
|
|
280
|
-
async debugTokens(): Promise<Record<TokenType, string | null>> {
|
|
281
|
-
console.log('🔍 [API Client] Debug: Current token status');
|
|
282
|
-
const tokens = await getAllTokens();
|
|
283
|
-
|
|
284
|
-
for (const [tokenType, token] of Object.entries(tokens)) {
|
|
285
|
-
if (token) {
|
|
286
|
-
console.log(`🔐 ${tokenType}: ${token.substring(0, 20)}... (length: ${token.length})`);
|
|
287
|
-
} else {
|
|
288
|
-
console.log(`📭 ${tokenType}: null`);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return tokens;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Set authentication triggers
|
|
297
|
-
*/
|
|
298
|
-
setAuthTriggers(triggers: AuthTriggers): void {
|
|
299
|
-
this.authTriggers = { ...this.authTriggers, ...triggers };
|
|
300
|
-
console.log('🔧 [API Client] Authentication triggers updated');
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Convenience methods for common HTTP verbs
|
|
305
|
-
*/
|
|
306
|
-
async get<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
|
|
307
|
-
return this.request<T>(endpoint, { ...options, method: 'GET' });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async post<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
|
|
311
|
-
return this.request<T>(endpoint, { ...options, method: 'POST', body });
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async put<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
|
|
315
|
-
return this.request<T>(endpoint, { ...options, method: 'PUT', body });
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async delete<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
|
|
319
|
-
return this.request<T>(endpoint, { ...options, method: 'DELETE' });
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// SINGLE INSTANCE - Global API client
|
|
324
|
-
export const apiClient = new ApiClient();
|
|
325
|
-
export { ApiClient };
|
|
326
|
-
|
|
327
|
-
// Export convenience functions
|
|
328
|
-
export const authenticatedRequest = apiClient.request.bind(apiClient);
|
|
329
|
-
export const apiGet = apiClient.get.bind(apiClient);
|
|
330
|
-
export const apiPost = apiClient.post.bind(apiClient);
|
|
331
|
-
export const apiPut = apiClient.put.bind(apiClient);
|
|
332
|
-
export const apiDelete = apiClient.delete.bind(apiClient);
|
|
333
|
-
|
|
334
|
-
// Export types for external use
|
|
335
|
-
export type { TokenType, ApiResponse, AuthTriggers };
|
|
336
|
-
|
|
1
|
+
import { getJWTForRoute, TokenType, getTokenTypeForRoute, clearJWT, getAllTokens } from './jwtStorageService';
|
|
2
|
+
import { API_CONFIG } from '../config/api';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Centralized API Client with Route-Based JWT Token Management
|
|
6
|
+
* CORRECTED APPROACH: Uses different JWT tokens for different route families
|
|
7
|
+
*
|
|
8
|
+
* TOKEN ROUTING:
|
|
9
|
+
* - Enoch JWT: /enoch/*, /api/auth/*, /mobile-training/enoch
|
|
10
|
+
* - Onairos JWT: /youtube/*, /gmail/*, social connections
|
|
11
|
+
* - Auth Token: Context-specific auth flows
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface ApiRequestOptions {
|
|
15
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
16
|
+
body?: any;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
requiresAuth?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ApiResponse<T = any> {
|
|
22
|
+
success: boolean;
|
|
23
|
+
data?: T;
|
|
24
|
+
error?: string;
|
|
25
|
+
status: number;
|
|
26
|
+
tokenType?: TokenType;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Global request counter for tracking in-flight requests
|
|
31
|
+
*/
|
|
32
|
+
let currentRequestId = 0;
|
|
33
|
+
const activeRequests = new Map<number, AbortController>();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Route-specific authentication triggers
|
|
37
|
+
*/
|
|
38
|
+
interface AuthTriggers {
|
|
39
|
+
triggerEnochReAuth?: () => Promise<void>;
|
|
40
|
+
triggerOnairosReAuth?: () => Promise<void>;
|
|
41
|
+
triggerAuthTokenRefresh?: () => Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create API client with route-based token management
|
|
46
|
+
*/
|
|
47
|
+
class ApiClient {
|
|
48
|
+
private baseUrl: string;
|
|
49
|
+
private authTriggers: AuthTriggers;
|
|
50
|
+
|
|
51
|
+
constructor(baseUrl: string = API_CONFIG.BASE_URL, authTriggers: AuthTriggers = {}) {
|
|
52
|
+
this.baseUrl = baseUrl;
|
|
53
|
+
this.authTriggers = authTriggers;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Make authenticated API request with route-based token selection
|
|
58
|
+
* CRITICAL: Uses the correct token type based on route
|
|
59
|
+
*/
|
|
60
|
+
async request<T = any>(
|
|
61
|
+
endpoint: string,
|
|
62
|
+
options: ApiRequestOptions = {}
|
|
63
|
+
): Promise<ApiResponse<T>> {
|
|
64
|
+
const requestId = ++currentRequestId;
|
|
65
|
+
const abortController = new AbortController();
|
|
66
|
+
activeRequests.set(requestId, abortController);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
console.log(`🔗 [API Client] Starting request ${requestId} to: ${endpoint}`);
|
|
70
|
+
|
|
71
|
+
// CRITICAL FIX: Determine which token type to use based on route
|
|
72
|
+
const tokenType = getTokenTypeForRoute(endpoint);
|
|
73
|
+
console.log(`🎯 [API Client] Route ${endpoint} requires ${tokenType} token`);
|
|
74
|
+
|
|
75
|
+
// Get route-specific headers
|
|
76
|
+
const headers = await this.getRequestHeaders(endpoint, options.requiresAuth !== false);
|
|
77
|
+
|
|
78
|
+
const fetchOptions: RequestInit = {
|
|
79
|
+
method: options.method || 'GET',
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
'User-Agent': 'OnairosSDK/1.0.0',
|
|
83
|
+
...headers,
|
|
84
|
+
...options.headers,
|
|
85
|
+
},
|
|
86
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
87
|
+
signal: abortController.signal,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
console.log(`🔐 [API Client] Request ${requestId} using ${tokenType} token`);
|
|
91
|
+
|
|
92
|
+
const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
|
|
93
|
+
|
|
94
|
+
console.log(`📡 [API Client] Response ${requestId} status:`, response.status);
|
|
95
|
+
|
|
96
|
+
// CRITICAL FIX: Route-specific 401 handler
|
|
97
|
+
if (response.status === 401) {
|
|
98
|
+
return await this.handle401Error(requestId, endpoint, tokenType, options);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = await response.json();
|
|
102
|
+
|
|
103
|
+
activeRequests.delete(requestId);
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: result.message || `Request failed with status ${response.status}`,
|
|
109
|
+
status: response.status,
|
|
110
|
+
tokenType,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
success: true,
|
|
116
|
+
data: result,
|
|
117
|
+
status: response.status,
|
|
118
|
+
tokenType,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
} catch (error) {
|
|
122
|
+
activeRequests.delete(requestId);
|
|
123
|
+
|
|
124
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
125
|
+
console.log(`🚫 [API Client] Request ${requestId} was cancelled`);
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
error: 'Request cancelled',
|
|
129
|
+
status: 0,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.error(`❌ [API Client] Request ${requestId} failed:`, error);
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
137
|
+
status: 0,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get request headers with route-appropriate token
|
|
144
|
+
* CRITICAL: Selects correct token based on route
|
|
145
|
+
*/
|
|
146
|
+
private async getRequestHeaders(endpoint: string, requiresAuth: boolean): Promise<Record<string, string>> {
|
|
147
|
+
if (!requiresAuth) {
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// CRITICAL FIX: Get token specifically for this route
|
|
152
|
+
const token = await getJWTForRoute(endpoint);
|
|
153
|
+
|
|
154
|
+
if (!token) {
|
|
155
|
+
const tokenType = getTokenTypeForRoute(endpoint);
|
|
156
|
+
throw new Error(`No ${tokenType} token available for route ${endpoint}. Please authenticate first.`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
'Authorization': token,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Handle 401 errors with route-specific re-authentication
|
|
166
|
+
* CRITICAL FIX: Different auth flows for different token types
|
|
167
|
+
*/
|
|
168
|
+
private async handle401Error<T>(
|
|
169
|
+
requestId: number,
|
|
170
|
+
endpoint: string,
|
|
171
|
+
tokenType: TokenType,
|
|
172
|
+
options: ApiRequestOptions
|
|
173
|
+
): Promise<ApiResponse<T>> {
|
|
174
|
+
console.error(`🚫 [API Client] Request ${requestId} received 401 for ${tokenType} token on route: ${endpoint}`);
|
|
175
|
+
|
|
176
|
+
// CRITICAL FIX: Clear only the specific token type that failed
|
|
177
|
+
console.log(`🔄 [API Client] Clearing ${tokenType} token due to 401 error`);
|
|
178
|
+
await clearJWT(tokenType);
|
|
179
|
+
|
|
180
|
+
// Cancel requests using the same token type
|
|
181
|
+
this.cancelRequestsForTokenType(tokenType, requestId);
|
|
182
|
+
|
|
183
|
+
// CRITICAL FIX: Route-specific re-authentication
|
|
184
|
+
await this.triggerReAuthForTokenType(tokenType, endpoint);
|
|
185
|
+
|
|
186
|
+
console.log(`🔑 [API Client] ${tokenType} token cleared, user needs to re-authenticate`);
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
error: `${tokenType} authentication expired for ${endpoint}. Please re-authenticate.`,
|
|
191
|
+
status: 401,
|
|
192
|
+
tokenType,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Trigger appropriate re-authentication based on token type
|
|
198
|
+
*/
|
|
199
|
+
private async triggerReAuthForTokenType(tokenType: TokenType, endpoint: string): Promise<void> {
|
|
200
|
+
console.log(`🔄 [API Client] Triggering re-auth for ${tokenType} token (endpoint: ${endpoint})`);
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
switch (tokenType) {
|
|
204
|
+
case TokenType.ENOCH:
|
|
205
|
+
if (this.authTriggers.triggerEnochReAuth) {
|
|
206
|
+
console.log('📧 [API Client] Triggering Enoch email re-verification');
|
|
207
|
+
await this.authTriggers.triggerEnochReAuth();
|
|
208
|
+
} else {
|
|
209
|
+
console.warn('⚠️ [API Client] No Enoch re-auth trigger configured');
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case TokenType.ONAIROS:
|
|
214
|
+
if (this.authTriggers.triggerOnairosReAuth) {
|
|
215
|
+
console.log('🔑 [API Client] Triggering Onairos re-authentication');
|
|
216
|
+
await this.authTriggers.triggerOnairosReAuth();
|
|
217
|
+
} else {
|
|
218
|
+
console.warn('⚠️ [API Client] No Onairos re-auth trigger configured');
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case TokenType.AUTH:
|
|
223
|
+
if (this.authTriggers.triggerAuthTokenRefresh) {
|
|
224
|
+
console.log('🔄 [API Client] Triggering auth token refresh');
|
|
225
|
+
await this.authTriggers.triggerAuthTokenRefresh();
|
|
226
|
+
} else {
|
|
227
|
+
console.warn('⚠️ [API Client] No auth token refresh trigger configured');
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error(`❌ [API Client] Failed to trigger re-auth for ${tokenType}:`, error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Cancel requests using a specific token type
|
|
238
|
+
*/
|
|
239
|
+
private cancelRequestsForTokenType(tokenType: TokenType, excludeRequestId?: number): void {
|
|
240
|
+
let cancelledCount = 0;
|
|
241
|
+
|
|
242
|
+
// Note: In a full implementation, we'd track which requests use which tokens
|
|
243
|
+
// For now, we cancel all other requests as a safety measure
|
|
244
|
+
for (const [requestId, controller] of activeRequests.entries()) {
|
|
245
|
+
if (requestId !== excludeRequestId) {
|
|
246
|
+
controller.abort();
|
|
247
|
+
activeRequests.delete(requestId);
|
|
248
|
+
cancelledCount++;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (cancelledCount > 0) {
|
|
253
|
+
console.log(`🚫 [API Client] Cancelled ${cancelledCount} requests due to ${tokenType} token failure`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Replace token for specific type and cancel related requests
|
|
259
|
+
*/
|
|
260
|
+
async replaceTokenForType(tokenType: TokenType, newToken: string): Promise<boolean> {
|
|
261
|
+
console.log(`🔄 [API Client] Replacing ${tokenType} token`);
|
|
262
|
+
|
|
263
|
+
// Cancel all in-flight requests using this token type
|
|
264
|
+
this.cancelRequestsForTokenType(tokenType);
|
|
265
|
+
|
|
266
|
+
// Replace token using storage service
|
|
267
|
+
const { replaceJWTAfterVerification } = await import('./jwtStorageService');
|
|
268
|
+
const replaced = await replaceJWTAfterVerification(tokenType, newToken);
|
|
269
|
+
|
|
270
|
+
if (replaced) {
|
|
271
|
+
console.log(`✅ [API Client] ${tokenType} token replaced and related requests cancelled`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return replaced;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Debug: Get all current tokens
|
|
279
|
+
*/
|
|
280
|
+
async debugTokens(): Promise<Record<TokenType, string | null>> {
|
|
281
|
+
console.log('🔍 [API Client] Debug: Current token status');
|
|
282
|
+
const tokens = await getAllTokens();
|
|
283
|
+
|
|
284
|
+
for (const [tokenType, token] of Object.entries(tokens)) {
|
|
285
|
+
if (token) {
|
|
286
|
+
console.log(`🔐 ${tokenType}: ${token.substring(0, 20)}... (length: ${token.length})`);
|
|
287
|
+
} else {
|
|
288
|
+
console.log(`📭 ${tokenType}: null`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return tokens;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Set authentication triggers
|
|
297
|
+
*/
|
|
298
|
+
setAuthTriggers(triggers: AuthTriggers): void {
|
|
299
|
+
this.authTriggers = { ...this.authTriggers, ...triggers };
|
|
300
|
+
console.log('🔧 [API Client] Authentication triggers updated');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Convenience methods for common HTTP verbs
|
|
305
|
+
*/
|
|
306
|
+
async get<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
|
|
307
|
+
return this.request<T>(endpoint, { ...options, method: 'GET' });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async post<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
|
|
311
|
+
return this.request<T>(endpoint, { ...options, method: 'POST', body });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async put<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
|
|
315
|
+
return this.request<T>(endpoint, { ...options, method: 'PUT', body });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async delete<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
|
|
319
|
+
return this.request<T>(endpoint, { ...options, method: 'DELETE' });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// SINGLE INSTANCE - Global API client
|
|
324
|
+
export const apiClient = new ApiClient();
|
|
325
|
+
export { ApiClient };
|
|
326
|
+
|
|
327
|
+
// Export convenience functions
|
|
328
|
+
export const authenticatedRequest = apiClient.request.bind(apiClient);
|
|
329
|
+
export const apiGet = apiClient.get.bind(apiClient);
|
|
330
|
+
export const apiPost = apiClient.post.bind(apiClient);
|
|
331
|
+
export const apiPut = apiClient.put.bind(apiClient);
|
|
332
|
+
export const apiDelete = apiClient.delete.bind(apiClient);
|
|
333
|
+
|
|
334
|
+
// Export types for external use
|
|
335
|
+
export type { TokenType, ApiResponse, AuthTriggers };
|
|
336
|
+
|
|
337
337
|
export default apiClient;
|