@onairos/react-native 3.1.0 → 3.1.2

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 (44) hide show
  1. package/lib/commonjs/components/UniversalOnboarding.js +22 -4
  2. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  3. package/lib/commonjs/index.js +13 -35
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/services/apiKeyService.js +133 -10
  6. package/lib/commonjs/services/apiKeyService.js.map +1 -1
  7. package/lib/commonjs/services/platformAuthService.js +26 -11
  8. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  9. package/lib/commonjs/types.js.map +1 -1
  10. package/lib/commonjs/utils/onairosApi.js +14 -6
  11. package/lib/commonjs/utils/onairosApi.js.map +1 -1
  12. package/lib/module/components/UniversalOnboarding.js +22 -4
  13. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  14. package/lib/module/index.js +1 -1
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/module/services/apiKeyService.js +128 -8
  17. package/lib/module/services/apiKeyService.js.map +1 -1
  18. package/lib/module/services/platformAuthService.js +27 -12
  19. package/lib/module/services/platformAuthService.js.map +1 -1
  20. package/lib/module/types.js.map +1 -1
  21. package/lib/module/utils/onairosApi.js +15 -7
  22. package/lib/module/utils/onairosApi.js.map +1 -1
  23. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  24. package/lib/typescript/index.d.ts +1 -1
  25. package/lib/typescript/index.d.ts.map +1 -1
  26. package/lib/typescript/services/apiKeyService.d.ts +18 -0
  27. package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
  28. package/lib/typescript/services/platformAuthService.d.ts +3 -3
  29. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  30. package/lib/typescript/types/index.d.ts +1 -0
  31. package/lib/typescript/types/index.d.ts.map +1 -1
  32. package/lib/typescript/types.d.ts +2 -0
  33. package/lib/typescript/types.d.ts.map +1 -1
  34. package/lib/typescript/utils/onairosApi.d.ts +4 -3
  35. package/lib/typescript/utils/onairosApi.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/UniversalOnboarding.tsx +23 -4
  38. package/src/index.ts +3 -0
  39. package/src/services/apiKeyService.ts +130 -0
  40. package/src/services/platformAuthService.ts +29 -11
  41. package/src/types/index.d.ts +5 -0
  42. package/src/types/index.ts +1 -0
  43. package/src/types.ts +2 -0
  44. package/src/utils/onairosApi.ts +17 -6
package/src/index.ts CHANGED
@@ -22,6 +22,9 @@ export { Onairos } from './components/Onairos';
22
22
  export {
23
23
  initializeApiKey,
24
24
  ADMIN_API_KEY,
25
+ extractUsernameFromJWT,
26
+ extractUserDataFromJWT,
27
+ decodeJWTPayload,
25
28
  } from './services/apiKeyService';
26
29
 
27
30
  // Authentication & PIN Management
@@ -352,6 +352,136 @@ export const clearJWT = async (): Promise<void> => {
352
352
  }
353
353
  };
354
354
 
355
+ /**
356
+ * React Native compatible base64 decoder
357
+ * @param str Base64 encoded string
358
+ * @returns Decoded string
359
+ */
360
+ const base64Decode = (str: string): string => {
361
+ // Simple base64 decoding for React Native
362
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
363
+ let result = '';
364
+ let i = 0;
365
+
366
+ str = str.replace(/[^A-Za-z0-9+/]/g, '');
367
+
368
+ while (i < str.length) {
369
+ const a = chars.indexOf(str.charAt(i++));
370
+ const b = chars.indexOf(str.charAt(i++));
371
+ const c = chars.indexOf(str.charAt(i++));
372
+ const d = chars.indexOf(str.charAt(i++));
373
+
374
+ const bitmap = (a << 18) | (b << 12) | (c << 6) | d;
375
+
376
+ result += String.fromCharCode((bitmap >> 16) & 255);
377
+ if (c !== 64) result += String.fromCharCode((bitmap >> 8) & 255);
378
+ if (d !== 64) result += String.fromCharCode(bitmap & 255);
379
+ }
380
+
381
+ return result;
382
+ };
383
+
384
+ /**
385
+ * Decode JWT token payload (React Native compatible)
386
+ * @param token JWT token string
387
+ * @returns Decoded payload or null if invalid
388
+ */
389
+ export const decodeJWTPayload = (token: string): any => {
390
+ try {
391
+ // Split JWT token (header.payload.signature)
392
+ const parts = token.split('.');
393
+ if (parts.length !== 3) {
394
+ console.error('❌ Invalid JWT token format');
395
+ return null;
396
+ }
397
+
398
+ // Decode payload (base64url to base64)
399
+ const payload = parts[1];
400
+ const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
401
+
402
+ // Add padding if needed
403
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
404
+
405
+ // Decode base64 to JSON using React Native compatible decoder
406
+ const decoded = base64Decode(padded);
407
+ return JSON.parse(decoded);
408
+ } catch (error) {
409
+ console.error('❌ Failed to decode JWT token:', error);
410
+ return null;
411
+ }
412
+ };
413
+
414
+ /**
415
+ * Extract username from JWT token
416
+ * @param token JWT token (optional, uses stored token if not provided)
417
+ * @returns Username or null if not found
418
+ */
419
+ export const extractUsernameFromJWT = (token?: string): string | null => {
420
+ try {
421
+ const jwtToken = token || userToken;
422
+ if (!jwtToken) {
423
+ console.warn('⚠️ No JWT token available for username extraction');
424
+ return null;
425
+ }
426
+
427
+ const payload = decodeJWTPayload(jwtToken);
428
+ if (!payload) {
429
+ return null;
430
+ }
431
+
432
+ // Try different possible username fields in order of preference
433
+ const username = payload.userName || payload.username || payload.userId || payload.email;
434
+
435
+ if (globalConfig?.enableLogging) {
436
+ console.log('👤 Extracted username from JWT:', username);
437
+ }
438
+
439
+ return username || null;
440
+ } catch (error) {
441
+ console.error('❌ Failed to extract username from JWT:', error);
442
+ return null;
443
+ }
444
+ };
445
+
446
+ /**
447
+ * Extract user data from JWT token
448
+ * @param token JWT token (optional, uses stored token if not provided)
449
+ * @returns User data object or null if not found
450
+ */
451
+ export const extractUserDataFromJWT = (token?: string): any => {
452
+ try {
453
+ const jwtToken = token || userToken;
454
+ if (!jwtToken) {
455
+ console.warn('⚠️ No JWT token available for user data extraction');
456
+ return null;
457
+ }
458
+
459
+ const payload = decodeJWTPayload(jwtToken);
460
+ if (!payload) {
461
+ return null;
462
+ }
463
+
464
+ const userData = {
465
+ id: payload.id,
466
+ email: payload.email,
467
+ userId: payload.userId,
468
+ userName: payload.userName || payload.username,
469
+ verified: payload.verified,
470
+ iat: payload.iat,
471
+ exp: payload.exp
472
+ };
473
+
474
+ if (globalConfig?.enableLogging) {
475
+ console.log('👤 Extracted user data from JWT:', userData);
476
+ }
477
+
478
+ return userData;
479
+ } catch (error) {
480
+ console.error('❌ Failed to extract user data from JWT:', error);
481
+ return null;
482
+ }
483
+ };
484
+
355
485
  /**
356
486
  * Check if user is authenticated with JWT token
357
487
  * @returns True if user has valid JWT token
@@ -1,7 +1,7 @@
1
1
  import { Platform, Linking } from 'react-native';
2
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
  import type { PlatformAuthConfig } from '../types';
4
- import { makeDeveloperRequest, getApiConfig, storeJWT } from './apiKeyService';
4
+ import { makeDeveloperRequest, getApiConfig, storeJWT, extractUsernameFromJWT } from './apiKeyService';
5
5
 
6
6
  // 🔑 CRITICAL: Use two-tier authentication system
7
7
  // - Developer API key for email verification requests
@@ -332,16 +332,20 @@ export const initiateNativeAuth = async (platform: string, username?: string): P
332
332
 
333
333
  // Configure Google Sign-In with better error handling
334
334
  try {
335
+ const youtubeConfig = PLATFORM_AUTH_CONFIG.youtube;
336
+ const webClientId = youtubeConfig.clientId || '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
337
+ const iosClientId = youtubeConfig.iosClientId || webClientId;
338
+
335
339
  await GoogleSignin.configure({
336
- webClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
337
- iosClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
340
+ webClientId: webClientId,
341
+ iosClientId: iosClientId,
338
342
  scopes: ['https://www.googleapis.com/auth/youtube.readonly'],
339
343
  offlineAccess: true,
340
344
  hostedDomain: '',
341
345
  forceCodeForRefreshToken: true,
342
346
  accountName: '',
343
347
  });
344
- console.log('✅ Google Sign-In configured successfully');
348
+ console.log('✅ Google Sign-In configured successfully with client ID:', webClientId.substring(0, 20) + '...');
345
349
  } catch (configError) {
346
350
  console.error('❌ Google Sign-In configuration failed:', configError);
347
351
  throw new Error(`Google Sign-In configuration failed: ${configError.message || configError}`);
@@ -794,9 +798,12 @@ export const updateGoogleClientIds = (config: {
794
798
  PLATFORM_AUTH_CONFIG.youtube = {
795
799
  ...PLATFORM_AUTH_CONFIG.youtube,
796
800
  clientId: config.webClientId || PLATFORM_AUTH_CONFIG.youtube.clientId,
801
+ iosClientId: config.iosClientId || PLATFORM_AUTH_CONFIG.youtube.iosClientId,
797
802
  };
798
803
 
799
804
  console.log('✅ Google client IDs updated successfully');
805
+ console.log(' - Web Client ID:', config.webClientId ? config.webClientId.substring(0, 20) + '...' : 'not provided');
806
+ console.log(' - iOS Client ID:', config.iosClientId ? config.iosClientId.substring(0, 20) + '...' : 'not provided');
800
807
  }
801
808
  };
802
809
 
@@ -989,23 +996,34 @@ export const disconnectPlatform = async (platform: string, username: string): Pr
989
996
  };
990
997
 
991
998
  /**
992
- * Store PIN for user (uses developer API key for now, should be JWT in future)
993
- * @param username Username
999
+ * Store PIN for user (uses JWT authentication and extracts username from JWT)
994
1000
  * @param pin User PIN
1001
+ * @param username Optional username (if not provided, extracts from JWT)
995
1002
  * @returns Promise with result
996
1003
  */
997
- export const storePIN = async (username: string, pin: string): Promise<{ success: boolean; error?: string }> => {
1004
+ export const storePIN = async (pin: string, username?: string): Promise<{ success: boolean; error?: string }> => {
998
1005
  try {
999
- console.log('🔐 Storing PIN for user:', username);
1006
+ // Extract username from JWT if not provided
1007
+ const userToStore = username || extractUsernameFromJWT();
1008
+
1009
+ if (!userToStore) {
1010
+ console.error('❌ No username available - either provide username or ensure JWT token is valid');
1011
+ return {
1012
+ success: false,
1013
+ error: 'No username available for PIN storage'
1014
+ };
1015
+ }
1016
+
1017
+ console.log('🔐 Storing PIN for user:', userToStore);
1000
1018
 
1001
- // Make authenticated request to store PIN
1019
+ // Make authenticated request to store PIN (using developer API key for now)
1002
1020
  const response = await makeDeveloperRequest('/store-pin/web', {
1003
1021
  method: 'POST',
1004
1022
  headers: {
1005
1023
  'Content-Type': 'application/json',
1006
1024
  },
1007
1025
  body: JSON.stringify({
1008
- username,
1026
+ username: userToStore,
1009
1027
  pin
1010
1028
  })
1011
1029
  });
@@ -1013,7 +1031,7 @@ export const storePIN = async (username: string, pin: string): Promise<{ success
1013
1031
  const data = await response.json();
1014
1032
 
1015
1033
  if (response.ok && data.success) {
1016
- console.log('✅ PIN stored successfully');
1034
+ console.log('✅ PIN stored successfully for user:', userToStore);
1017
1035
  return { success: true };
1018
1036
  } else {
1019
1037
  console.error('❌ Failed to store PIN:', data.error);
@@ -243,6 +243,11 @@ declare module '@onairos/react-native' {
243
243
  // SDK Initialization Functions
244
244
  export function initializeApiKey(config: any): Promise<void>;
245
245
  export const ADMIN_API_KEY: string;
246
+
247
+ // JWT Token Functions
248
+ export function extractUsernameFromJWT(token?: string): string | null;
249
+ export function extractUserDataFromJWT(token?: string): any;
250
+ export function decodeJWTPayload(token: string): any;
246
251
 
247
252
  // Programmatic Flow
248
253
  export function executeOnairosFlow(config: any): Promise<any>;
@@ -211,6 +211,7 @@ export interface PlatformAuthConfig {
211
211
  authEndpoint: string;
212
212
  color: string;
213
213
  clientId?: string;
214
+ iosClientId?: string;
214
215
  redirectUri?: string;
215
216
  scope?: string;
216
217
  responseType?: string;
package/src/types.ts CHANGED
@@ -18,6 +18,7 @@ export interface TestModeOptions {
18
18
  // Optional tweaks
19
19
  fastTraining?: boolean; // Speed up training simulation
20
20
  skipRealConnections?: boolean; // Allow mock platform connections
21
+ skipApiCalls?: boolean; // Skip API calls entirely (for pure UI testing)
21
22
  }
22
23
 
23
24
  export interface ApiKeyConfig {
@@ -239,6 +240,7 @@ export interface PlatformAuthConfig {
239
240
  authEndpoint: string;
240
241
  color: string;
241
242
  clientId?: string;
243
+ iosClientId?: string;
242
244
  redirectUri?: string;
243
245
  scope?: string;
244
246
  responseType?: string;
@@ -2,7 +2,7 @@ import { Platform } from 'react-native';
2
2
  import NetInfo from '@react-native-community/netinfo';
3
3
  import { OnairosCredentials } from './secureStorage';
4
4
  import { logDebug } from './debugHelper';
5
- import { getJWT, makeUserRequest, makeDeveloperRequest, isUserAuthenticated } from '../services/apiKeyService';
5
+ import { getJWT, makeUserRequest, makeDeveloperRequest, isUserAuthenticated, extractUsernameFromJWT } from '../services/apiKeyService';
6
6
 
7
7
  // API response types
8
8
  export interface ApiResponse<T = any> {
@@ -352,16 +352,27 @@ export const updatePlatformConnections = async (
352
352
  };
353
353
 
354
354
  /**
355
- * Store user PIN (uses JWT)
355
+ * Store user PIN (uses JWT and extracts username from JWT)
356
356
  */
357
357
  export const storePIN = async (
358
- username: string,
359
358
  pin: string,
360
- options = { debug: false }
359
+ options: { debug?: boolean; username?: string } = {}
361
360
  ): Promise<ApiResponse<any>> => {
361
+ const { debug = false, username } = options;
362
+
363
+ // Extract username from JWT if not provided
364
+ const userToStore = username || extractUsernameFromJWT();
365
+
366
+ if (!userToStore) {
367
+ throw new ApiError(
368
+ 'No username available - either provide username or ensure JWT token is valid',
369
+ 'auth_error'
370
+ );
371
+ }
372
+
362
373
  return makeUserApiRequest('store-pin/mobile', 'POST', {
363
- data: { username, pin },
364
- debug: options.debug,
374
+ data: { username: userToStore, pin },
375
+ debug,
365
376
  });
366
377
  };
367
378