@onairos/react-native 3.0.75 → 3.1.1

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 (43) 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/index.js +18 -6
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/services/apiKeyService.js +401 -27
  6. package/lib/commonjs/services/apiKeyService.js.map +1 -1
  7. package/lib/commonjs/services/platformAuthService.js +130 -299
  8. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  9. package/lib/commonjs/utils/onairosApi.js +151 -71
  10. package/lib/commonjs/utils/onairosApi.js.map +1 -1
  11. package/lib/commonjs/utils/secureStorage.js +123 -1
  12. package/lib/commonjs/utils/secureStorage.js.map +1 -1
  13. package/lib/module/components/EmailVerificationModal.js +7 -5
  14. package/lib/module/components/EmailVerificationModal.js.map +1 -1
  15. package/lib/module/index.js +4 -2
  16. package/lib/module/index.js.map +1 -1
  17. package/lib/module/services/apiKeyService.js +384 -22
  18. package/lib/module/services/apiKeyService.js.map +1 -1
  19. package/lib/module/services/platformAuthService.js +127 -295
  20. package/lib/module/services/platformAuthService.js.map +1 -1
  21. package/lib/module/utils/onairosApi.js +147 -70
  22. package/lib/module/utils/onairosApi.js.map +1 -1
  23. package/lib/module/utils/secureStorage.js +116 -0
  24. package/lib/module/utils/secureStorage.js.map +1 -1
  25. package/lib/typescript/components/EmailVerificationModal.d.ts.map +1 -1
  26. package/lib/typescript/index.d.ts +2 -2
  27. package/lib/typescript/index.d.ts.map +1 -1
  28. package/lib/typescript/services/apiKeyService.d.ts +68 -2
  29. package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
  30. package/lib/typescript/services/platformAuthService.d.ts +29 -14
  31. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  32. package/lib/typescript/utils/onairosApi.d.ts +25 -10
  33. package/lib/typescript/utils/onairosApi.d.ts.map +1 -1
  34. package/lib/typescript/utils/secureStorage.d.ts +31 -0
  35. package/lib/typescript/utils/secureStorage.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/EmailVerificationModal.tsx +9 -5
  38. package/src/index.ts +4 -1
  39. package/src/services/apiKeyService.ts +412 -18
  40. package/src/services/platformAuthService.ts +219 -421
  41. package/src/types/index.d.ts +11 -5
  42. package/src/utils/onairosApi.ts +162 -74
  43. package/src/utils/secureStorage.ts +122 -0
@@ -213,11 +213,12 @@ declare module '@onairos/react-native' {
213
213
  export class OAuthWebView extends Component<OAuthWebViewProps> {}
214
214
 
215
215
  // Authentication & PIN Management Functions
216
- export function storePinAfterBiometric(
217
- username: string,
218
- pin: string,
219
- jwtToken?: string
220
- ): Promise<PinStorageResult>;
216
+ // TODO: Commented out temporarily - biometric functionality disabled
217
+ // export function storePinAfterBiometric(
218
+ // username: string,
219
+ // pin: string,
220
+ // jwtToken?: string
221
+ // ): Promise<PinStorageResult>;
221
222
 
222
223
  export function getStoredJwtToken(): Promise<string | null>;
223
224
 
@@ -242,6 +243,11 @@ declare module '@onairos/react-native' {
242
243
  // SDK Initialization Functions
243
244
  export function initializeApiKey(config: any): Promise<void>;
244
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;
245
251
 
246
252
  // Programmatic Flow
247
253
  export function executeOnairosFlow(config: any): Promise<any>;
@@ -2,13 +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
-
6
- // API configuration
7
- const API_CONFIG = {
8
- baseUrl: 'https://api2.onairos.uk',
9
- version: 'v1',
10
- timeout: 30000, // 30 seconds
11
- };
5
+ import { getJWT, makeUserRequest, makeDeveloperRequest, isUserAuthenticated, extractUsernameFromJWT } from '../services/apiKeyService';
12
6
 
13
7
  // API response types
14
8
  export interface ApiResponse<T = any> {
@@ -54,44 +48,121 @@ const checkNetworkConnection = async (): Promise<boolean> => {
54
48
  };
55
49
 
56
50
  /**
57
- * Build API request headers
51
+ * Make user-authenticated API request using JWT token
58
52
  */
59
- const buildHeaders = (accessToken?: string, additionalHeaders = {}): Record<string, string> => {
60
- const headers: Record<string, string> = {
61
- 'Content-Type': 'application/json',
62
- 'Accept': 'application/json',
63
- 'User-Agent': `OnairosReactNative/${Platform.OS}/${Platform.Version}`,
64
- ...additionalHeaders,
65
- };
53
+ const makeUserApiRequest = async <T>(
54
+ endpoint: string,
55
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
56
+ options: {
57
+ data?: any;
58
+ headers?: Record<string, string>;
59
+ debug?: boolean;
60
+ } = {}
61
+ ): Promise<ApiResponse<T>> => {
62
+ const { data, headers = {}, debug = false } = options;
66
63
 
67
- if (accessToken) {
68
- headers['Authorization'] = `Bearer ${accessToken}`;
64
+ try {
65
+ // Check for network connectivity
66
+ const isConnected = await checkNetworkConnection();
67
+ if (!isConnected) {
68
+ throw new ApiError(
69
+ 'No network connection available',
70
+ 'network_error'
71
+ );
72
+ }
73
+
74
+ // Check if user is authenticated
75
+ if (!isUserAuthenticated()) {
76
+ throw new ApiError(
77
+ 'User not authenticated. Please verify email first.',
78
+ 'auth_error'
79
+ );
80
+ }
81
+
82
+ // Log request information if debug mode is enabled
83
+ if (debug) {
84
+ logDebug('User API Request', {
85
+ endpoint,
86
+ method,
87
+ headers: { ...headers, Authorization: '[REDACTED]' },
88
+ data: data || null,
89
+ });
90
+ }
91
+
92
+ // Make authenticated request using JWT token
93
+ const response = await makeUserRequest(endpoint, {
94
+ method,
95
+ headers,
96
+ ...(data ? { body: JSON.stringify(data) } : {}),
97
+ });
98
+
99
+ // Parse response as JSON
100
+ const responseData = await response.json();
101
+
102
+ // Log response if debug mode is enabled
103
+ if (debug) {
104
+ logDebug('User API Response', {
105
+ status: response.status,
106
+ data: responseData,
107
+ });
108
+ }
109
+
110
+ // Handle API error responses
111
+ if (!response.ok) {
112
+ const errorType: ApiErrorType =
113
+ response.status === 401 || response.status === 403 ? 'auth_error' :
114
+ response.status === 400 ? 'validation_error' :
115
+ response.status >= 500 ? 'server_error' : 'unknown_error';
116
+
117
+ throw new ApiError(
118
+ responseData.error?.message || 'API request failed',
119
+ errorType,
120
+ responseData.error?.code,
121
+ responseData.error?.details
122
+ );
123
+ }
124
+
125
+ return responseData as ApiResponse<T>;
126
+ } catch (error) {
127
+ // Handle specific error types
128
+ if (error instanceof ApiError) {
129
+ throw error;
130
+ }
131
+
132
+ if (error.name === 'AbortError') {
133
+ throw new ApiError('Request timed out', 'timeout_error');
134
+ }
135
+
136
+ // Log error if debug mode is enabled
137
+ if (debug) {
138
+ logDebug('User API Error', {
139
+ endpoint,
140
+ method,
141
+ error: error.message || 'Unknown error',
142
+ });
143
+ }
144
+
145
+ // Return a generic error for all other cases
146
+ throw new ApiError(
147
+ error.message || 'An unexpected error occurred',
148
+ 'unknown_error'
149
+ );
69
150
  }
70
-
71
- return headers;
72
151
  };
73
152
 
74
153
  /**
75
- * Make API request with proper error handling
154
+ * Make developer-authenticated API request using API key
76
155
  */
77
- const apiRequest = async <T>(
156
+ const makeDeveloperApiRequest = async <T>(
78
157
  endpoint: string,
79
158
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
80
159
  options: {
81
160
  data?: any;
82
- accessToken?: string;
83
161
  headers?: Record<string, string>;
84
- timeout?: number;
85
162
  debug?: boolean;
86
163
  } = {}
87
164
  ): Promise<ApiResponse<T>> => {
88
- const {
89
- data,
90
- accessToken,
91
- headers = {},
92
- timeout = API_CONFIG.timeout,
93
- debug = false,
94
- } = options;
165
+ const { data, headers = {}, debug = false } = options;
95
166
 
96
167
  try {
97
168
  // Check for network connectivity
@@ -103,45 +174,29 @@ const apiRequest = async <T>(
103
174
  );
104
175
  }
105
176
 
106
- // Build request URL
107
- const url = `${API_CONFIG.baseUrl}/${API_CONFIG.version}/${endpoint}`;
108
-
109
177
  // Log request information if debug mode is enabled
110
178
  if (debug) {
111
- logDebug('API Request', {
112
- url,
179
+ logDebug('Developer API Request', {
180
+ endpoint,
113
181
  method,
114
- headers: { ...buildHeaders(accessToken, headers), Authorization: accessToken ? '[REDACTED]' : undefined },
182
+ headers: { ...headers, Authorization: '[REDACTED]' },
115
183
  data: data || null,
116
184
  });
117
185
  }
118
186
 
119
- // Set up request options
120
- const fetchOptions: RequestInit = {
187
+ // Make authenticated request using developer API key
188
+ const response = await makeDeveloperRequest(endpoint, {
121
189
  method,
122
- headers: buildHeaders(accessToken, headers),
190
+ headers,
123
191
  ...(data ? { body: JSON.stringify(data) } : {}),
124
- };
125
-
126
- // Create fetch request with timeout
127
- const controller = new AbortController();
128
- const timeoutId = setTimeout(() => controller.abort(), timeout);
129
-
130
- // Execute request
131
- const response = await fetch(url, {
132
- ...fetchOptions,
133
- signal: controller.signal,
134
192
  });
135
193
 
136
- // Clear timeout
137
- clearTimeout(timeoutId);
138
-
139
194
  // Parse response as JSON
140
195
  const responseData = await response.json();
141
196
 
142
197
  // Log response if debug mode is enabled
143
198
  if (debug) {
144
- logDebug('API Response', {
199
+ logDebug('Developer API Response', {
145
200
  status: response.status,
146
201
  data: responseData,
147
202
  });
@@ -175,7 +230,7 @@ const apiRequest = async <T>(
175
230
 
176
231
  // Log error if debug mode is enabled
177
232
  if (debug) {
178
- logDebug('API Error', {
233
+ logDebug('Developer API Error', {
179
234
  endpoint,
180
235
  method,
181
236
  error: error.message || 'Unknown error',
@@ -191,14 +246,14 @@ const apiRequest = async <T>(
191
246
  };
192
247
 
193
248
  /**
194
- * Validate user credentials with the API
249
+ * Validate user credentials with the API (uses JWT)
195
250
  */
196
251
  export const validateCredentials = async (
197
252
  username: string,
198
253
  options = { debug: false }
199
254
  ): Promise<{ isValid: boolean; message?: string }> => {
200
255
  try {
201
- const response = await apiRequest<{ isValid: boolean }>('auth/validate', 'POST', {
256
+ const response = await makeUserApiRequest<{ isValid: boolean }>('auth/validate', 'POST', {
202
257
  data: { username },
203
258
  debug: options.debug,
204
259
  });
@@ -216,13 +271,13 @@ export const validateCredentials = async (
216
271
  };
217
272
 
218
273
  /**
219
- * Create a new user account
274
+ * Create a new user account (uses developer API key)
220
275
  */
221
276
  export const createAccount = async (
222
277
  credentials: Partial<OnairosCredentials>,
223
278
  options = { debug: false }
224
279
  ): Promise<ApiResponse<{ accessToken: string; userId: string }>> => {
225
- return apiRequest('auth/register', 'POST', {
280
+ return makeDeveloperApiRequest('auth/register', 'POST', {
226
281
  data: {
227
282
  username: credentials.username,
228
283
  platforms: credentials.platforms,
@@ -232,13 +287,13 @@ export const createAccount = async (
232
287
  };
233
288
 
234
289
  /**
235
- * Authenticate with the API using credentials
290
+ * Authenticate with the API using credentials (uses JWT)
236
291
  */
237
292
  export const authenticate = async (
238
293
  credentials: Partial<OnairosCredentials>,
239
294
  options = { debug: false }
240
295
  ): Promise<ApiResponse<{ accessToken: string; refreshToken: string }>> => {
241
- return apiRequest('auth/login', 'POST', {
296
+ return makeUserApiRequest('auth/login', 'POST', {
242
297
  data: {
243
298
  username: credentials.username,
244
299
  pin: credentials.userPin,
@@ -248,56 +303,89 @@ export const authenticate = async (
248
303
  };
249
304
 
250
305
  /**
251
- * Refresh authentication token
306
+ * Refresh authentication token (uses JWT)
252
307
  */
253
308
  export const refreshToken = async (
254
309
  refreshToken: string,
255
310
  options = { debug: false }
256
311
  ): Promise<ApiResponse<{ accessToken: string; refreshToken: string }>> => {
257
- return apiRequest('auth/refresh', 'POST', {
312
+ return makeUserApiRequest('auth/refresh', 'POST', {
258
313
  data: { refreshToken },
259
314
  debug: options.debug,
260
315
  });
261
316
  };
262
317
 
263
318
  /**
264
- * Get user's connected platform data
319
+ * Get user's connected platform data (uses JWT)
265
320
  */
266
321
  export const getPlatformData = async (
267
- accessToken: string,
268
322
  platform: string,
269
323
  options = { debug: false }
270
324
  ): Promise<ApiResponse<any>> => {
271
- return apiRequest(`platforms/${platform}/data`, 'GET', {
272
- accessToken,
325
+ return makeUserApiRequest(`platforms/${platform}/data`, 'GET', {
273
326
  debug: options.debug,
274
327
  });
275
328
  };
276
329
 
277
330
  /**
278
- * Get user profile information
331
+ * Get user profile information (uses JWT)
279
332
  */
280
333
  export const getUserProfile = async (
281
- accessToken: string,
282
334
  options = { debug: false }
283
335
  ): Promise<ApiResponse<any>> => {
284
- return apiRequest('user/profile', 'GET', {
285
- accessToken,
336
+ return makeUserApiRequest('user/profile', 'GET', {
286
337
  debug: options.debug,
287
338
  });
288
339
  };
289
340
 
290
341
  /**
291
- * Update user platform connections
342
+ * Update user platform connections (uses JWT)
292
343
  */
293
344
  export const updatePlatformConnections = async (
294
- accessToken: string,
295
345
  platforms: Record<string, any>,
296
346
  options = { debug: false }
297
347
  ): Promise<ApiResponse<any>> => {
298
- return apiRequest('user/platforms', 'PUT', {
299
- accessToken,
348
+ return makeUserApiRequest('user/platforms', 'PUT', {
300
349
  data: { platforms },
301
350
  debug: options.debug,
302
351
  });
303
352
  };
353
+
354
+ /**
355
+ * Store user PIN (uses JWT and extracts username from JWT)
356
+ */
357
+ export const storePIN = async (
358
+ pin: string,
359
+ options: { debug?: boolean; username?: string } = {}
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
+
373
+ return makeUserApiRequest('store-pin/mobile', 'POST', {
374
+ data: { username: userToStore, pin },
375
+ debug,
376
+ });
377
+ };
378
+
379
+ /**
380
+ * Get current JWT token
381
+ */
382
+ export const getCurrentUserToken = async (): Promise<string | null> => {
383
+ return await getJWT();
384
+ };
385
+
386
+ /**
387
+ * Check if user is authenticated with JWT
388
+ */
389
+ export const isCurrentUserAuthenticated = (): boolean => {
390
+ return isUserAuthenticated();
391
+ };
@@ -1,6 +1,10 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { sha256 } from './crypto';
3
3
  import { STORAGE_KEYS } from '../constants';
4
+ import AsyncStorage from '@react-native-async-storage/async-storage';
5
+
6
+ // JWT token storage key
7
+ const JWT_TOKEN_KEY = 'onairos_jwt_token';
4
8
 
5
9
  // Define OnairosCredentials interface locally to avoid circular dependencies
6
10
  export interface OnairosCredentials {
@@ -237,3 +241,121 @@ export const verifyCredentials = async (username: string): Promise<boolean> => {
237
241
  return false;
238
242
  }
239
243
  };
244
+
245
+ /**
246
+ * JWT Token Storage Functions
247
+ * These functions handle JWT tokens from email verification
248
+ *
249
+ * Note: Main JWT token management is handled in apiKeyService.ts
250
+ * These are utility functions for backwards compatibility
251
+ */
252
+
253
+ /**
254
+ * Retrieve JWT token
255
+ * @returns JWT token or null if not found
256
+ */
257
+ export const getJWT = async (): Promise<string | null> => {
258
+ try {
259
+ const token = await AsyncStorage.getItem(JWT_TOKEN_KEY);
260
+ return token;
261
+ } catch (error) {
262
+ console.error('❌ Failed to retrieve JWT token:', error);
263
+ return null;
264
+ }
265
+ };
266
+
267
+ /**
268
+ * Check if JWT token exists
269
+ * @returns True if JWT token exists
270
+ */
271
+ export const hasJWT = async (): Promise<boolean> => {
272
+ try {
273
+ const token = await AsyncStorage.getItem(JWT_TOKEN_KEY);
274
+ return !!token;
275
+ } catch (error) {
276
+ console.error('❌ Failed to check JWT token:', error);
277
+ return false;
278
+ }
279
+ };
280
+
281
+ /**
282
+ * Clear JWT token
283
+ */
284
+ export const clearJWT = async (): Promise<boolean> => {
285
+ try {
286
+ await AsyncStorage.removeItem(JWT_TOKEN_KEY);
287
+ console.log('🗑️ JWT token cleared');
288
+ return true;
289
+ } catch (error) {
290
+ console.error('❌ Failed to clear JWT token:', error);
291
+ return false;
292
+ }
293
+ };
294
+
295
+ /**
296
+ * Simple base64 decoder for React Native
297
+ */
298
+ const base64Decode = (str: string): string => {
299
+ // Add padding if needed
300
+ while (str.length % 4) {
301
+ str += '=';
302
+ }
303
+
304
+ // Simple base64 decoding
305
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
306
+ let result = '';
307
+
308
+ for (let i = 0; i < str.length; i += 4) {
309
+ const a = chars.indexOf(str.charAt(i));
310
+ const b = chars.indexOf(str.charAt(i + 1));
311
+ const c = chars.indexOf(str.charAt(i + 2));
312
+ const d = chars.indexOf(str.charAt(i + 3));
313
+
314
+ const bitmap = (a << 18) | (b << 12) | (c << 6) | d;
315
+
316
+ result += String.fromCharCode((bitmap >> 16) & 255);
317
+ if (c !== 64) result += String.fromCharCode((bitmap >> 8) & 255);
318
+ if (d !== 64) result += String.fromCharCode(bitmap & 255);
319
+ }
320
+
321
+ return result;
322
+ };
323
+
324
+ /**
325
+ * Get JWT token payload (decoded)
326
+ * @returns Decoded JWT payload or null if invalid
327
+ */
328
+ export const getJWTPayload = async (): Promise<any | null> => {
329
+ try {
330
+ const token = await getJWT();
331
+ if (!token) return null;
332
+
333
+ // Decode JWT payload (middle part of token)
334
+ const parts = token.split('.');
335
+ if (parts.length !== 3) return null;
336
+
337
+ const decodedPayload = base64Decode(parts[1]);
338
+ const payload = JSON.parse(decodedPayload);
339
+ return payload;
340
+ } catch (error) {
341
+ console.error('❌ Failed to decode JWT payload:', error);
342
+ return null;
343
+ }
344
+ };
345
+
346
+ /**
347
+ * Check if JWT token is expired
348
+ * @returns True if token is expired or invalid
349
+ */
350
+ export const isJWTExpired = async (): Promise<boolean> => {
351
+ try {
352
+ const payload = await getJWTPayload();
353
+ if (!payload || !payload.exp) return true;
354
+
355
+ const currentTime = Math.floor(Date.now() / 1000);
356
+ return payload.exp < currentTime;
357
+ } catch (error) {
358
+ console.error('❌ Failed to check JWT expiration:', error);
359
+ return true;
360
+ }
361
+ };