@onairos/react-native 3.0.74 → 3.1.0

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 (49) hide show
  1. package/lib/commonjs/components/EmailVerificationModal.js +7 -5
  2. package/lib/commonjs/components/EmailVerificationModal.js.map +1 -1
  3. package/lib/commonjs/components/UniversalOnboarding.js +10 -1
  4. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  5. package/lib/commonjs/index.js +0 -6
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/services/apiKeyService.js +278 -27
  8. package/lib/commonjs/services/apiKeyService.js.map +1 -1
  9. package/lib/commonjs/services/platformAuthService.js +127 -290
  10. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  11. package/lib/commonjs/utils/onairosApi.js +143 -71
  12. package/lib/commonjs/utils/onairosApi.js.map +1 -1
  13. package/lib/commonjs/utils/secureStorage.js +123 -1
  14. package/lib/commonjs/utils/secureStorage.js.map +1 -1
  15. package/lib/module/components/EmailVerificationModal.js +7 -5
  16. package/lib/module/components/EmailVerificationModal.js.map +1 -1
  17. package/lib/module/components/UniversalOnboarding.js +11 -2
  18. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  19. package/lib/module/index.js +3 -1
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/services/apiKeyService.js +264 -22
  22. package/lib/module/services/apiKeyService.js.map +1 -1
  23. package/lib/module/services/platformAuthService.js +125 -287
  24. package/lib/module/services/platformAuthService.js.map +1 -1
  25. package/lib/module/utils/onairosApi.js +139 -70
  26. package/lib/module/utils/onairosApi.js.map +1 -1
  27. package/lib/module/utils/secureStorage.js +116 -0
  28. package/lib/module/utils/secureStorage.js.map +1 -1
  29. package/lib/typescript/components/EmailVerificationModal.d.ts.map +1 -1
  30. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  31. package/lib/typescript/index.d.ts +1 -1
  32. package/lib/typescript/index.d.ts.map +1 -1
  33. package/lib/typescript/services/apiKeyService.d.ts +50 -2
  34. package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
  35. package/lib/typescript/services/platformAuthService.d.ts +29 -14
  36. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  37. package/lib/typescript/utils/onairosApi.d.ts +24 -10
  38. package/lib/typescript/utils/onairosApi.d.ts.map +1 -1
  39. package/lib/typescript/utils/secureStorage.d.ts +31 -0
  40. package/lib/typescript/utils/secureStorage.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/components/EmailVerificationModal.tsx +9 -5
  43. package/src/components/UniversalOnboarding.tsx +11 -1
  44. package/src/index.ts +1 -1
  45. package/src/services/apiKeyService.ts +282 -18
  46. package/src/services/platformAuthService.ts +216 -412
  47. package/src/types/index.d.ts +6 -5
  48. package/src/utils/onairosApi.ts +151 -74
  49. package/src/utils/secureStorage.ts +122 -0
@@ -1,4 +1,5 @@
1
1
  import type { OnairosConfig, ApiKeyValidationResult } from '../types';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
2
3
 
3
4
  // Admin key for backend validation
4
5
  export const ADMIN_API_KEY = 'OnairosIsAUnicorn2025';
@@ -10,30 +11,33 @@ export enum ApiKeyType {
10
11
  INVALID = 'invalid'
11
12
  }
12
13
 
14
+ // JWT token storage key
15
+ const JWT_TOKEN_KEY = 'onairos_jwt_token';
16
+
13
17
  /**
14
- * API Key Service for Onairos React Native SDK
18
+ * Two-Tier Authentication Service for Onairos React Native SDK
15
19
  *
16
- * This service handles API key validation, management, and authentication
17
- * following standard SDK patterns for secure API key handling.
20
+ * This service implements the two-tier authentication system:
21
+ * 1. Developer API Keys: For app-level operations (email verification, app registration)
22
+ * 2. JWT User Tokens: For user-level operations (PIN storage, user profile)
18
23
  *
19
24
  * How it works:
20
- * 1. Initialize with API key and configuration
21
- * 2. Validate API key with the Onairos backend
22
- * 3. Cache validation results for performance
23
- * 4. Include API key in all authenticated requests
24
- * 5. Handle API key errors gracefully with developer-friendly messages
25
+ * 1. Initialize with developer API key
26
+ * 2. Use API key for email verification requests
27
+ * 3. Store JWT token from email verification response
28
+ * 4. Use JWT token for user-authenticated requests
29
+ * 5. Handle token expiration gracefully
25
30
  *
26
31
  * 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
32
+ * - Developer routes: Authorization: Bearer ${API_KEY}
33
+ * - User routes: Authorization: Bearer ${JWT_TOKEN}
31
34
  */
32
35
 
33
36
  // Global configuration state
34
37
  let globalConfig: OnairosConfig | null = null;
35
38
  let validationCache: Map<string, { result: ApiKeyValidationResult; timestamp: number }> = new Map();
36
39
  let isInitialized = false;
40
+ let userToken: string | null = null;
37
41
 
38
42
  // Cache duration (5 minutes)
39
43
  const CACHE_DURATION = 5 * 60 * 1000;
@@ -46,15 +50,15 @@ const API_ENDPOINTS = {
46
50
  };
47
51
 
48
52
  /**
49
- * Initialize the SDK with API key and configuration
50
- * @param config API configuration including API key
53
+ * Initialize the SDK with developer API key
54
+ * @param config API configuration including developer API key
51
55
  */
52
56
  export const initializeApiKey = async (config: OnairosConfig): Promise<void> => {
53
57
  try {
54
- console.log('🔑 Initializing Onairos SDK with API key...');
58
+ console.log('🔑 Initializing Onairos SDK with developer API key...');
55
59
 
56
60
  if (!config.apiKey) {
57
- throw new Error('API key is required for SDK initialization');
61
+ throw new Error('Developer API key is required for SDK initialization');
58
62
  }
59
63
 
60
64
  if (config.apiKey.length < 32) {
@@ -80,17 +84,24 @@ export const initializeApiKey = async (config: OnairosConfig): Promise<void> =>
80
84
  });
81
85
  }
82
86
 
83
- // Validate the API key
87
+ // Validate the developer API key
84
88
  const validation = await validateApiKey(config.apiKey);
85
89
 
86
90
  if (!validation.isValid) {
87
- throw new Error(`API key validation failed: ${validation.error}`);
91
+ throw new Error(`Developer API key validation failed: ${validation.error}`);
88
92
  }
89
93
 
94
+ // Try to load existing JWT token
95
+ await loadJWT();
96
+
90
97
  isInitialized = true;
91
98
 
92
99
  if (globalConfig.enableLogging) {
93
100
  console.log('✅ Onairos SDK initialized successfully');
101
+ console.log('🔑 Developer API key ready for app-level operations');
102
+ if (userToken) {
103
+ console.log('🎫 User JWT token loaded from storage');
104
+ }
94
105
  if (validation.permissions) {
95
106
  console.log('🔐 API Key Permissions:', validation.permissions);
96
107
  }
@@ -284,6 +295,71 @@ export const isApiKeyInitialized = (): boolean => {
284
295
  return isInitialized && globalConfig !== null;
285
296
  };
286
297
 
298
+ /**
299
+ * Store JWT token securely after email verification
300
+ * @param token JWT token from email verification response
301
+ */
302
+ export const storeJWT = async (token: string): Promise<void> => {
303
+ try {
304
+ await AsyncStorage.setItem(JWT_TOKEN_KEY, token);
305
+ userToken = token;
306
+
307
+ if (globalConfig?.enableLogging) {
308
+ console.log('🎫 JWT token stored successfully');
309
+ }
310
+ } catch (error) {
311
+ console.error('❌ Failed to store JWT token:', error);
312
+ throw error;
313
+ }
314
+ };
315
+
316
+ /**
317
+ * Load JWT token from storage
318
+ * @returns JWT token or null if not found
319
+ */
320
+ export const loadJWT = async (): Promise<string | null> => {
321
+ try {
322
+ const token = await AsyncStorage.getItem(JWT_TOKEN_KEY);
323
+ userToken = token;
324
+ return token;
325
+ } catch (error) {
326
+ console.error('❌ Failed to load JWT token:', error);
327
+ return null;
328
+ }
329
+ };
330
+
331
+ /**
332
+ * Get current JWT token
333
+ * @returns JWT token or null if not available
334
+ */
335
+ export const getJWT = (): string | null => {
336
+ return userToken;
337
+ };
338
+
339
+ /**
340
+ * Clear JWT token (on logout or token expiration)
341
+ */
342
+ export const clearJWT = async (): Promise<void> => {
343
+ try {
344
+ await AsyncStorage.removeItem(JWT_TOKEN_KEY);
345
+ userToken = null;
346
+
347
+ if (globalConfig?.enableLogging) {
348
+ console.log('🗑️ JWT token cleared');
349
+ }
350
+ } catch (error) {
351
+ console.error('❌ Failed to clear JWT token:', error);
352
+ }
353
+ };
354
+
355
+ /**
356
+ * Check if user is authenticated with JWT token
357
+ * @returns True if user has valid JWT token
358
+ */
359
+ export const isUserAuthenticated = (): boolean => {
360
+ return !!userToken;
361
+ };
362
+
287
363
  /**
288
364
  * Get authenticated headers for API requests
289
365
  * @returns Headers object with Authorization and other required headers
@@ -306,6 +382,46 @@ export const getAuthHeaders = (): Record<string, string> => {
306
382
  };
307
383
  };
308
384
 
385
+ /**
386
+ * Get authentication headers for developer API requests
387
+ * @returns Headers with developer API key
388
+ */
389
+ export const getDeveloperAuthHeaders = (): Record<string, string> => {
390
+ if (!globalConfig?.apiKey) {
391
+ throw new Error('SDK not initialized. Call initializeApiKey() first.');
392
+ }
393
+
394
+ const keyType = getApiKeyType(globalConfig.apiKey);
395
+
396
+ return {
397
+ 'Content-Type': 'application/json',
398
+ 'Authorization': `Bearer ${globalConfig.apiKey}`,
399
+ 'User-Agent': 'OnairosSDK/1.0.0',
400
+ 'X-SDK-Version': '3.0.72',
401
+ 'X-SDK-Environment': globalConfig.environment || 'production',
402
+ 'X-API-Key-Type': keyType,
403
+ 'X-Timestamp': new Date().toISOString(),
404
+ };
405
+ };
406
+
407
+ /**
408
+ * Get authentication headers for user JWT requests
409
+ * @returns Headers with user JWT token
410
+ */
411
+ export const getUserAuthHeaders = (): Record<string, string> => {
412
+ if (!userToken) {
413
+ throw new Error('User not authenticated. Please verify email first.');
414
+ }
415
+
416
+ return {
417
+ 'Content-Type': 'application/json',
418
+ 'Authorization': `Bearer ${userToken}`,
419
+ 'User-Agent': 'OnairosSDK/1.0.0',
420
+ 'X-SDK-Version': '3.0.72',
421
+ 'X-SDK-Environment': globalConfig?.environment || 'production',
422
+ };
423
+ };
424
+
309
425
  /**
310
426
  * Make an authenticated API request
311
427
  * @param endpoint The API endpoint (relative to base URL)
@@ -380,6 +496,154 @@ export const makeAuthenticatedRequest = async (
380
496
  }
381
497
  };
382
498
 
499
+ /**
500
+ * Make authenticated request with developer API key
501
+ * @param endpoint The API endpoint
502
+ * @param options Fetch options
503
+ * @returns Response promise
504
+ */
505
+ export const makeDeveloperRequest = async (
506
+ endpoint: string,
507
+ options: RequestInit = {}
508
+ ): Promise<Response> => {
509
+ if (!isApiKeyInitialized()) {
510
+ throw new Error('SDK not initialized. Call initializeApiKey() first.');
511
+ }
512
+
513
+ const config = getApiConfig()!;
514
+ const baseUrl = API_ENDPOINTS[config.environment || 'production'];
515
+ const url = `${baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
516
+
517
+ // Merge developer authentication headers
518
+ const headers = {
519
+ ...getDeveloperAuthHeaders(),
520
+ ...(options.headers || {}),
521
+ };
522
+
523
+ // Add timeout
524
+ const controller = new AbortController();
525
+ const timeoutId = setTimeout(() => controller.abort(), config.timeout || 30000);
526
+
527
+ try {
528
+ if (config.enableLogging) {
529
+ console.log(`🌐 Making developer request to: ${endpoint}`);
530
+ }
531
+
532
+ const response = await fetch(url, {
533
+ ...options,
534
+ headers,
535
+ signal: controller.signal,
536
+ });
537
+
538
+ clearTimeout(timeoutId);
539
+
540
+ if (config.enableLogging) {
541
+ console.log(`📡 Developer request response: ${response.status} for ${endpoint}`);
542
+ }
543
+
544
+ // Handle API key errors
545
+ if (response.status === 401) {
546
+ console.error('❌ Developer API key authentication failed');
547
+ throw new Error('Invalid or expired API key');
548
+ }
549
+
550
+ if (response.status === 403) {
551
+ console.error('❌ Developer API key permissions insufficient');
552
+ throw new Error('Insufficient API key permissions');
553
+ }
554
+
555
+ if (response.status === 429) {
556
+ console.error('❌ API rate limit exceeded');
557
+ throw new Error('Rate limit exceeded');
558
+ }
559
+
560
+ return response;
561
+ } catch (error) {
562
+ clearTimeout(timeoutId);
563
+
564
+ if (error.name === 'AbortError') {
565
+ console.error('⏱️ Request timeout for:', endpoint);
566
+ throw new Error('Request timeout');
567
+ }
568
+
569
+ throw error;
570
+ }
571
+ };
572
+
573
+ /**
574
+ * Make authenticated request with user JWT token
575
+ * @param endpoint The API endpoint
576
+ * @param options Fetch options
577
+ * @returns Response promise
578
+ */
579
+ export const makeUserRequest = async (
580
+ endpoint: string,
581
+ options: RequestInit = {}
582
+ ): Promise<Response> => {
583
+ if (!isUserAuthenticated()) {
584
+ await loadJWT(); // Try to load from storage
585
+ }
586
+
587
+ if (!isUserAuthenticated()) {
588
+ throw new Error('User not authenticated. Please verify email first.');
589
+ }
590
+
591
+ const config = getApiConfig() || { environment: 'production', timeout: 30000, enableLogging: false };
592
+ const baseUrl = API_ENDPOINTS[config.environment || 'production'];
593
+ const url = `${baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
594
+
595
+ // Merge user authentication headers
596
+ const headers = {
597
+ ...getUserAuthHeaders(),
598
+ ...(options.headers || {}),
599
+ };
600
+
601
+ // Add timeout
602
+ const controller = new AbortController();
603
+ const timeoutId = setTimeout(() => controller.abort(), config.timeout || 30000);
604
+
605
+ try {
606
+ if (config.enableLogging) {
607
+ console.log(`🌐 Making user request to: ${endpoint}`);
608
+ }
609
+
610
+ const response = await fetch(url, {
611
+ ...options,
612
+ headers,
613
+ signal: controller.signal,
614
+ });
615
+
616
+ clearTimeout(timeoutId);
617
+
618
+ if (config.enableLogging) {
619
+ console.log(`📡 User request response: ${response.status} for ${endpoint}`);
620
+ }
621
+
622
+ // Handle JWT token errors
623
+ if (response.status === 401) {
624
+ console.error('❌ JWT token authentication failed - token may be expired');
625
+ await clearJWT(); // Clear expired token
626
+ throw new Error('Authentication expired. Please verify email again.');
627
+ }
628
+
629
+ if (response.status === 403) {
630
+ console.error('❌ JWT token permissions insufficient');
631
+ throw new Error('Insufficient permissions for this operation');
632
+ }
633
+
634
+ return response;
635
+ } catch (error) {
636
+ clearTimeout(timeoutId);
637
+
638
+ if (error.name === 'AbortError') {
639
+ console.error('⏱️ Request timeout for:', endpoint);
640
+ throw new Error('Request timeout');
641
+ }
642
+
643
+ throw error;
644
+ }
645
+ };
646
+
383
647
  /**
384
648
  * Clear the API key validation cache
385
649
  */