@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.
- package/lib/commonjs/components/EmailVerificationModal.js +7 -5
- package/lib/commonjs/components/EmailVerificationModal.js.map +1 -1
- package/lib/commonjs/index.js +18 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/services/apiKeyService.js +401 -27
- package/lib/commonjs/services/apiKeyService.js.map +1 -1
- package/lib/commonjs/services/platformAuthService.js +130 -299
- package/lib/commonjs/services/platformAuthService.js.map +1 -1
- package/lib/commonjs/utils/onairosApi.js +151 -71
- package/lib/commonjs/utils/onairosApi.js.map +1 -1
- package/lib/commonjs/utils/secureStorage.js +123 -1
- package/lib/commonjs/utils/secureStorage.js.map +1 -1
- package/lib/module/components/EmailVerificationModal.js +7 -5
- package/lib/module/components/EmailVerificationModal.js.map +1 -1
- package/lib/module/index.js +4 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/services/apiKeyService.js +384 -22
- package/lib/module/services/apiKeyService.js.map +1 -1
- package/lib/module/services/platformAuthService.js +127 -295
- package/lib/module/services/platformAuthService.js.map +1 -1
- package/lib/module/utils/onairosApi.js +147 -70
- package/lib/module/utils/onairosApi.js.map +1 -1
- package/lib/module/utils/secureStorage.js +116 -0
- package/lib/module/utils/secureStorage.js.map +1 -1
- package/lib/typescript/components/EmailVerificationModal.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/services/apiKeyService.d.ts +68 -2
- package/lib/typescript/services/apiKeyService.d.ts.map +1 -1
- package/lib/typescript/services/platformAuthService.d.ts +29 -14
- package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
- package/lib/typescript/utils/onairosApi.d.ts +25 -10
- package/lib/typescript/utils/onairosApi.d.ts.map +1 -1
- package/lib/typescript/utils/secureStorage.d.ts +31 -0
- package/lib/typescript/utils/secureStorage.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/EmailVerificationModal.tsx +9 -5
- package/src/index.ts +4 -1
- package/src/services/apiKeyService.ts +412 -18
- package/src/services/platformAuthService.ts +219 -421
- package/src/types/index.d.ts +11 -5
- package/src/utils/onairosApi.ts +162 -74
- package/src/utils/secureStorage.ts +122 -0
package/src/types/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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>;
|
package/src/utils/onairosApi.ts
CHANGED
|
@@ -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
|
-
*
|
|
51
|
+
* Make user-authenticated API request using JWT token
|
|
58
52
|
*/
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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
|
|
154
|
+
* Make developer-authenticated API request using API key
|
|
76
155
|
*/
|
|
77
|
-
const
|
|
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
|
-
|
|
179
|
+
logDebug('Developer API Request', {
|
|
180
|
+
endpoint,
|
|
113
181
|
method,
|
|
114
|
-
headers: { ...
|
|
182
|
+
headers: { ...headers, Authorization: '[REDACTED]' },
|
|
115
183
|
data: data || null,
|
|
116
184
|
});
|
|
117
185
|
}
|
|
118
186
|
|
|
119
|
-
//
|
|
120
|
-
const
|
|
187
|
+
// Make authenticated request using developer API key
|
|
188
|
+
const response = await makeDeveloperRequest(endpoint, {
|
|
121
189
|
method,
|
|
122
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
};
|