@oxyhq/services 5.5.4 → 5.5.6

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 (32) hide show
  1. package/lib/commonjs/core/AuthManager.js +352 -0
  2. package/lib/commonjs/core/AuthManager.js.map +1 -0
  3. package/lib/commonjs/core/index.js +13 -2
  4. package/lib/commonjs/core/index.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +87 -456
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/useAuthFetch.js +22 -60
  8. package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -1
  9. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  10. package/lib/module/core/AuthManager.js +347 -0
  11. package/lib/module/core/AuthManager.js.map +1 -0
  12. package/lib/module/core/index.js +6 -2
  13. package/lib/module/core/index.js.map +1 -1
  14. package/lib/module/ui/context/OxyContext.js +87 -456
  15. package/lib/module/ui/context/OxyContext.js.map +1 -1
  16. package/lib/module/ui/hooks/useAuthFetch.js +22 -60
  17. package/lib/module/ui/hooks/useAuthFetch.js.map +1 -1
  18. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  19. package/lib/typescript/core/AuthManager.d.ts +100 -0
  20. package/lib/typescript/core/AuthManager.d.ts.map +1 -0
  21. package/lib/typescript/core/index.d.ts +6 -2
  22. package/lib/typescript/core/index.d.ts.map +1 -1
  23. package/lib/typescript/ui/context/OxyContext.d.ts +7 -10
  24. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  25. package/lib/typescript/ui/hooks/useAuthFetch.d.ts +4 -9
  26. package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/core/AuthManager.ts +366 -0
  29. package/src/core/index.ts +7 -2
  30. package/src/ui/context/OxyContext.tsx +99 -508
  31. package/src/ui/hooks/useAuthFetch.ts +22 -60
  32. package/src/ui/screens/SessionManagementScreen.tsx +9 -9
@@ -1,49 +1,44 @@
1
1
  import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode, useMemo } from 'react';
2
2
  import { OxyServices } from '../../core';
3
+ import { AuthManager, AuthState } from '../../core/AuthManager';
3
4
  import { User } from '../../models/interfaces';
4
- import { SecureLoginResponse, SecureClientSession, MinimalUserData } from '../../models/secureSession';
5
- import { DeviceManager } from '../../utils/deviceManager';
5
+ import { SecureClientSession } from '../../models/secureSession';
6
6
 
7
- // Define the context shape
7
+ // Context interface with session management support
8
8
  export interface OxyContextState {
9
- // Authentication state
10
- user: User | null; // Current active user (loaded from server)
11
- minimalUser: MinimalUserData | null; // Minimal user data for UI
12
- sessions: SecureClientSession[]; // All active sessions
13
- activeSessionId: string | null;
14
- isAuthenticated: boolean; // Single source of truth for authentication - use this instead of service methods
9
+ // Auth state from AuthManager
10
+ isAuthenticated: boolean;
11
+ user: User | null;
15
12
  isLoading: boolean;
16
13
  error: string | null;
17
14
 
15
+ // Session management
16
+ sessions: any[];
17
+ activeSessionId: string | null;
18
+
18
19
  // Auth methods
19
20
  login: (username: string, password: string, deviceName?: string) => Promise<User>;
20
21
  logout: (targetSessionId?: string) => Promise<void>;
21
- logoutAll: () => Promise<void>;
22
22
  signUp: (username: string, email: string, password: string) => Promise<User>;
23
23
 
24
- // Multi-session methods
24
+ // Session management methods
25
+ refreshSessions: () => Promise<void>;
25
26
  switchSession: (sessionId: string) => Promise<void>;
26
27
  removeSession: (sessionId: string) => Promise<void>;
27
- refreshSessions: () => Promise<void>;
28
-
29
- // Device management methods
30
- getDeviceSessions: () => Promise<any[]>;
31
- logoutAllDeviceSessions: () => Promise<void>;
32
- updateDeviceName: (deviceName: string) => Promise<void>;
28
+ logoutAll: () => Promise<void>;
33
29
 
34
- // Access to services
30
+ // Access to services and auth manager
35
31
  oxyServices: OxyServices;
36
- bottomSheetRef?: React.RefObject<any>;
32
+ authManager: AuthManager;
37
33
 
38
- // Methods to directly control the bottom sheet
34
+ // UI controls
35
+ bottomSheetRef?: React.RefObject<any>;
39
36
  showBottomSheet?: (screenOrConfig?: string | { screen: string; props?: Record<string, any> }) => void;
40
37
  hideBottomSheet?: () => void;
41
38
  }
42
39
 
43
- // Create the context with default values
44
40
  const OxyContext = createContext<OxyContextState | null>(null);
45
41
 
46
- // Props for the OxyContextProvider
47
42
  export interface OxyContextProviderProps {
48
43
  children: ReactNode;
49
44
  oxyServices: OxyServices;
@@ -52,7 +47,7 @@ export interface OxyContextProviderProps {
52
47
  bottomSheetRef?: React.RefObject<any>;
53
48
  }
54
49
 
55
- // Platform storage implementation
50
+ // Storage implementation
56
51
  interface StorageInterface {
57
52
  getItem: (key: string) => Promise<string | null>;
58
53
  setItem: (key: string, value: string) => Promise<void>;
@@ -60,7 +55,6 @@ interface StorageInterface {
60
55
  clear: () => Promise<void>;
61
56
  }
62
57
 
63
- // Web localStorage implementation
64
58
  class WebStorage implements StorageInterface {
65
59
  async getItem(key: string): Promise<string | null> {
66
60
  return localStorage.getItem(key);
@@ -79,15 +73,12 @@ class WebStorage implements StorageInterface {
79
73
  }
80
74
  }
81
75
 
82
- // React Native AsyncStorage implementation
83
76
  let AsyncStorage: StorageInterface;
84
77
 
85
- // Determine the platform and set up storage
86
78
  const isReactNative = (): boolean => {
87
79
  return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
88
80
  };
89
81
 
90
- // Get appropriate storage for the platform
91
82
  const getStorage = async (): Promise<StorageInterface> => {
92
83
  if (isReactNative()) {
93
84
  if (!AsyncStorage) {
@@ -101,16 +92,9 @@ const getStorage = async (): Promise<StorageInterface> => {
101
92
  }
102
93
  return AsyncStorage;
103
94
  }
104
-
105
95
  return new WebStorage();
106
96
  };
107
97
 
108
- // Storage keys for secure sessions
109
- const getSecureStorageKeys = (prefix = 'oxy_secure') => ({
110
- sessions: `${prefix}_sessions`, // Array of SecureClientSession objects
111
- activeSessionId: `${prefix}_active_session_id`, // ID of currently active session
112
- });
113
-
114
98
  export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
115
99
  children,
116
100
  oxyServices,
@@ -118,17 +102,29 @@ export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
118
102
  onAuthStateChange,
119
103
  bottomSheetRef,
120
104
  }) => {
121
- // Authentication state
122
- const [user, setUser] = useState<User | null>(null);
123
- const [minimalUser, setMinimalUser] = useState<MinimalUserData | null>(null);
124
- const [sessions, setSessions] = useState<SecureClientSession[]>([]);
125
- const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
126
- const [isLoading, setIsLoading] = useState(true);
127
- const [error, setError] = useState<string | null>(null);
128
105
  const [storage, setStorage] = useState<StorageInterface | null>(null);
129
-
130
- // Storage keys (memoized to prevent infinite loops)
131
- const keys = useMemo(() => getSecureStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
106
+ const [authState, setAuthState] = useState<AuthState>({
107
+ isAuthenticated: false,
108
+ accessToken: null,
109
+ user: null,
110
+ activeSessionId: null,
111
+ sessions: [],
112
+ isLoading: true,
113
+ error: null,
114
+ });
115
+
116
+ // Create AuthManager instance
117
+ const authManager = useMemo(() => {
118
+ return new AuthManager({
119
+ oxyServices,
120
+ onStateChange: (newState) => {
121
+ setAuthState(newState);
122
+ if (onAuthStateChange) {
123
+ onAuthStateChange(newState.user);
124
+ }
125
+ },
126
+ });
127
+ }, [oxyServices, onAuthStateChange]);
132
128
 
133
129
  // Initialize storage
134
130
  useEffect(() => {
@@ -138,499 +134,109 @@ export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
138
134
  setStorage(platformStorage);
139
135
  } catch (error) {
140
136
  console.error('Failed to initialize storage:', error);
141
- setError('Failed to initialize storage');
137
+ setAuthState(prev => ({ ...prev, error: 'Failed to initialize storage', isLoading: false }));
142
138
  }
143
139
  };
144
-
145
140
  initStorage();
146
141
  }, []);
147
142
 
148
- // Effect to initialize authentication state
143
+ // Initialize authentication
149
144
  useEffect(() => {
150
145
  const initAuth = async () => {
151
146
  if (!storage) return;
152
147
 
153
- setIsLoading(true);
154
148
  try {
155
- // Load stored sessions
156
- const sessionsData = await storage.getItem(keys.sessions);
157
- const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
158
-
159
- console.log('SecureAuth - sessionsData:', sessionsData);
160
- console.log('SecureAuth - activeSessionId:', storedActiveSessionId);
161
-
162
- if (sessionsData) {
163
- const parsedSessions: SecureClientSession[] = JSON.parse(sessionsData);
164
-
165
- // Migrate old session format to include user info
166
- const migratedSessions: SecureClientSession[] = [];
167
- let shouldUpdateStorage = false;
168
-
169
- for (const session of parsedSessions) {
170
- if (!session.userId || !session.username) {
171
- // Session is missing user info, try to fetch it
172
- try {
173
- const sessionUser = await oxyServices.getUserBySession(session.sessionId);
174
- migratedSessions.push({
175
- ...session,
176
- userId: sessionUser.id,
177
- username: sessionUser.username
178
- });
179
- shouldUpdateStorage = true;
180
- console.log(`Migrated session ${session.sessionId} for user ${sessionUser.username}`);
181
- } catch (error) {
182
- // Session might be invalid, skip it
183
- console.log(`Removing invalid session ${session.sessionId}:`, error);
184
- shouldUpdateStorage = true;
185
- }
186
- } else {
187
- // Session already has user info
188
- migratedSessions.push(session);
189
- }
190
- }
191
-
192
- // Update storage if we made changes
193
- if (shouldUpdateStorage) {
194
- await saveSessionsToStorage(migratedSessions);
195
- }
196
-
197
- setSessions(migratedSessions);
198
-
199
- if (storedActiveSessionId && migratedSessions.length > 0) {
200
- const activeSession = migratedSessions.find(s => s.sessionId === storedActiveSessionId);
201
-
202
- if (activeSession) {
203
- console.log('SecureAuth - activeSession found:', activeSession);
204
-
205
- // Validate session
206
- try {
207
- const validation = await oxyServices.validateSession(activeSession.sessionId);
208
-
209
- if (validation.valid) {
210
- console.log('SecureAuth - session validated successfully');
211
- setActiveSessionId(activeSession.sessionId);
212
-
213
- // Get access token for API calls
214
- await oxyServices.getTokenBySession(activeSession.sessionId);
215
-
216
- // Load full user data
217
- const fullUser = await oxyServices.getUserBySession(activeSession.sessionId);
218
- setUser(fullUser);
219
- setMinimalUser({
220
- id: fullUser.id,
221
- username: fullUser.username,
222
- avatar: fullUser.avatar
223
- });
224
-
225
- if (onAuthStateChange) {
226
- onAuthStateChange(fullUser);
227
- }
228
- } else {
229
- console.log('SecureAuth - session invalid, removing');
230
- await removeInvalidSession(activeSession.sessionId);
231
- }
232
- } catch (error) {
233
- console.error('SecureAuth - session validation error:', error);
234
- await removeInvalidSession(activeSession.sessionId);
235
- }
236
- }
237
- }
238
- }
239
- } catch (err) {
240
- console.error('Secure auth initialization error:', err);
241
- await clearAllStorage();
242
- } finally {
243
- setIsLoading(false);
149
+ const activeSessionId = await storage.getItem(`${storageKeyPrefix}_active_session_id`);
150
+ await authManager.initialize(activeSessionId);
151
+ } catch (error) {
152
+ console.error('Auth initialization failed:', error);
153
+ await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
244
154
  }
245
155
  };
246
156
 
247
157
  if (storage) {
248
158
  initAuth();
249
159
  }
250
- }, [storage, oxyServices, keys, onAuthStateChange]);
251
-
252
- // Remove invalid session
253
- const removeInvalidSession = useCallback(async (sessionId: string): Promise<void> => {
254
- const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
255
- setSessions(filteredSessions);
256
- await saveSessionsToStorage(filteredSessions);
257
-
258
- // If there are other sessions, switch to the first one
259
- if (filteredSessions.length > 0) {
260
- await switchToSession(filteredSessions[0].sessionId);
261
- } else {
262
- // No valid sessions left
263
- setActiveSessionId(null);
264
- setUser(null);
265
- setMinimalUser(null);
266
- await storage?.removeItem(keys.activeSessionId);
267
-
268
- if (onAuthStateChange) {
269
- onAuthStateChange(null);
270
- }
271
- }
272
- }, [sessions, storage, keys, onAuthStateChange]);
273
-
274
- // Save sessions to storage
275
- const saveSessionsToStorage = useCallback(async (sessionsList: SecureClientSession[]): Promise<void> => {
276
- if (!storage) return;
277
- await storage.setItem(keys.sessions, JSON.stringify(sessionsList));
278
- }, [storage, keys.sessions]);
279
-
280
- // Save active session ID to storage
281
- const saveActiveSessionId = useCallback(async (sessionId: string): Promise<void> => {
282
- if (!storage) return;
283
- await storage.setItem(keys.activeSessionId, sessionId);
284
- }, [storage, keys.activeSessionId]);
285
-
286
- // Clear all storage
287
- const clearAllStorage = useCallback(async (): Promise<void> => {
288
- if (!storage) return;
289
- try {
290
- await storage.removeItem(keys.sessions);
291
- await storage.removeItem(keys.activeSessionId);
292
- } catch (err) {
293
- console.error('Clear secure storage error:', err);
294
- }
295
- }, [storage, keys]);
160
+ }, [storage, authManager, storageKeyPrefix]);
296
161
 
297
- // Switch to a different session
298
- const switchToSession = useCallback(async (sessionId: string): Promise<void> => {
299
- try {
300
- setIsLoading(true);
162
+ // Auth methods that integrate with storage
163
+ const login = useCallback(async (username: string, password: string, deviceName?: string): Promise<User> => {
164
+ await authManager.login(username, password, deviceName);
301
165
 
302
- // Get access token for this session
303
- await oxyServices.getTokenBySession(sessionId);
304
-
305
- // Load full user data
306
- const fullUser = await oxyServices.getUserBySession(sessionId);
307
-
308
- setActiveSessionId(sessionId);
309
- setUser(fullUser);
310
- setMinimalUser({
311
- id: fullUser.id,
312
- username: fullUser.username,
313
- avatar: fullUser.avatar
314
- });
315
-
316
- await saveActiveSessionId(sessionId);
317
-
318
- if (onAuthStateChange) {
319
- onAuthStateChange(fullUser);
320
- }
321
- } catch (error) {
322
- console.error('Switch session error:', error);
323
- setError('Failed to switch session');
324
- } finally {
325
- setIsLoading(false);
166
+ // Save session ID to storage
167
+ const sessionId = authManager.getActiveSessionId();
168
+ if (sessionId && storage) {
169
+ await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
326
170
  }
327
- }, [oxyServices, onAuthStateChange, saveActiveSessionId]);
328
-
329
- // Secure login method
330
- const login = async (username: string, password: string, deviceName?: string): Promise<User> => {
331
- if (!storage) throw new Error('Storage not initialized');
332
-
333
- setIsLoading(true);
334
- setError(null);
335
-
336
- try {
337
- // Get device fingerprint for enhanced device identification
338
- const deviceFingerprint = DeviceManager.getDeviceFingerprint();
339
-
340
- // Get or generate persistent device info
341
- const deviceInfo = await DeviceManager.getDeviceInfo();
342
-
343
- console.log('SecureAuth - Using device fingerprint:', deviceFingerprint);
344
- console.log('SecureAuth - Using device ID:', deviceInfo.deviceId);
345
-
346
- const response: SecureLoginResponse = await oxyServices.secureLogin(
347
- username,
348
- password,
349
- deviceName || deviceInfo.deviceName || DeviceManager.getDefaultDeviceName(),
350
- deviceFingerprint
351
- );
352
-
353
- // Create client session object with user info for duplicate detection
354
- const clientSession: SecureClientSession = {
355
- sessionId: response.sessionId,
356
- deviceId: response.deviceId,
357
- expiresAt: response.expiresAt,
358
- lastActive: new Date().toISOString(),
359
- userId: response.user.id,
360
- username: response.user.username
361
- };
362
-
363
- // Check if this user already has a session (prevent duplicate accounts)
364
- const existingUserSessionIndex = sessions.findIndex(s =>
365
- s.userId === response.user.id || s.username === response.user.username
366
- );
367
-
368
- let updatedSessions: SecureClientSession[];
369
-
370
- if (existingUserSessionIndex !== -1) {
371
- // User already has a session - replace it with the new one (reused session scenario)
372
- const existingSession = sessions[existingUserSessionIndex];
373
- updatedSessions = [...sessions];
374
- updatedSessions[existingUserSessionIndex] = clientSession;
375
-
376
- console.log(`Reusing/updating existing session for user ${response.user.username}. Previous session: ${existingSession.sessionId}, New session: ${response.sessionId}`);
377
-
378
- // If the replaced session was the active one, update active session
379
- if (activeSessionId === existingSession.sessionId) {
380
- setActiveSessionId(response.sessionId);
381
- await saveActiveSessionId(response.sessionId);
382
- }
383
- } else {
384
- // Add new session for new user
385
- updatedSessions = [...sessions, clientSession];
386
- console.log(`Added new session for user ${response.user.username} on device ${response.deviceId}`);
387
- }
388
-
389
- setSessions(updatedSessions);
390
- await saveSessionsToStorage(updatedSessions);
391
-
392
- // Set as active session
393
- setActiveSessionId(response.sessionId);
394
- await saveActiveSessionId(response.sessionId);
395
-
396
- // Get access token for API calls
397
- await oxyServices.getTokenBySession(response.sessionId);
398
171
 
399
- // Load full user data
400
- const fullUser = await oxyServices.getUserBySession(response.sessionId);
401
- setUser(fullUser);
402
- setMinimalUser(response.user);
172
+ return authManager.getCurrentUser();
173
+ }, [authManager, storage, storageKeyPrefix]);
403
174
 
404
- if (onAuthStateChange) {
405
- onAuthStateChange(fullUser);
406
- }
175
+ const logout = useCallback(async (targetSessionId?: string): Promise<void> => {
176
+ await authManager.logout(targetSessionId);
407
177
 
408
- return fullUser;
409
- } catch (error: any) {
410
- setError(error.message || 'Login failed');
411
- throw error;
412
- } finally {
413
- setIsLoading(false);
178
+ // Clear from storage if logging out current session
179
+ if (!targetSessionId && storage) {
180
+ await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
414
181
  }
415
- };
182
+ }, [authManager, storage, storageKeyPrefix]);
416
183
 
417
- // Logout method
418
- const logout = async (targetSessionId?: string): Promise<void> => {
419
- if (!activeSessionId) return;
420
-
421
- try {
422
- const sessionToLogout = targetSessionId || activeSessionId;
423
- await oxyServices.logoutSecureSession(activeSessionId, sessionToLogout);
424
-
425
- // Remove session from local storage
426
- const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
427
- setSessions(filteredSessions);
428
- await saveSessionsToStorage(filteredSessions);
429
-
430
- // If logging out active session
431
- if (sessionToLogout === activeSessionId) {
432
- if (filteredSessions.length > 0) {
433
- // Switch to another session
434
- await switchToSession(filteredSessions[0].sessionId);
435
- } else {
436
- // No sessions left
437
- setActiveSessionId(null);
438
- setUser(null);
439
- setMinimalUser(null);
440
- await storage?.removeItem(keys.activeSessionId);
441
-
442
- if (onAuthStateChange) {
443
- onAuthStateChange(null);
444
- }
445
- }
446
- }
447
- } catch (error) {
448
- console.error('Logout error:', error);
449
- setError('Logout failed');
450
- }
451
- };
452
-
453
- // Logout all sessions
454
- const logoutAll = async (): Promise<void> => {
455
- console.log('logoutAll called with activeSessionId:', activeSessionId);
456
-
457
- if (!activeSessionId) {
458
- console.error('No active session ID found, cannot logout all');
459
- setError('No active session found');
460
- throw new Error('No active session found');
461
- }
462
-
463
- if (!oxyServices) {
464
- console.error('OxyServices not initialized');
465
- setError('Service not available');
466
- throw new Error('Service not available');
467
- }
468
-
469
- try {
470
- console.log('Calling oxyServices.logoutAllSecureSessions with sessionId:', activeSessionId);
471
- await oxyServices.logoutAllSecureSessions(activeSessionId);
472
- console.log('logoutAllSecureSessions completed successfully');
473
-
474
- // Clear all local data
475
- setSessions([]);
476
- setActiveSessionId(null);
477
- setUser(null);
478
- setMinimalUser(null);
479
- await clearAllStorage();
480
- console.log('Local storage cleared');
481
-
482
- if (onAuthStateChange) {
483
- onAuthStateChange(null);
484
- console.log('Auth state change callback called');
485
- }
486
- } catch (error) {
487
- console.error('Logout all error:', error);
488
- setError(`Logout all failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
489
- throw error;
490
- }
491
- };
492
-
493
- // Sign up method
494
- const signUp = async (username: string, email: string, password: string): Promise<User> => {
495
- if (!storage) throw new Error('Storage not initialized');
496
-
497
- setIsLoading(true);
498
- setError(null);
499
-
500
- try {
501
- // Create new account using the OxyServices signUp method
502
- const response = await oxyServices.signUp(username, email, password);
503
-
504
- console.log('SignUp successful:', response);
184
+ const refreshSessions = useCallback(async (): Promise<void> => {
185
+ await authManager.refreshSessions();
186
+ }, [authManager]);
505
187
 
506
- // Now log the user in securely to create a session
507
- // This will handle the session creation and device registration
508
- const user = await login(username, password);
188
+ const switchSession = useCallback(async (sessionId: string): Promise<void> => {
189
+ await authManager.switchSession(sessionId);
509
190
 
510
- return user;
511
- } catch (error: any) {
512
- setError(error.message || 'Sign up failed');
513
- throw error;
514
- } finally {
515
- setIsLoading(false);
191
+ // Update storage with new active session
192
+ if (storage) {
193
+ await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
516
194
  }
517
- };
195
+ }, [authManager, storage, storageKeyPrefix]);
518
196
 
519
- // Switch session method
520
- const switchSession = async (sessionId: string): Promise<void> => {
521
- await switchToSession(sessionId);
522
- };
197
+ const removeSession = useCallback(async (sessionId: string): Promise<void> => {
198
+ await authManager.removeSession(sessionId);
199
+ }, [authManager]);
523
200
 
524
- // Remove session method
525
- const removeSession = async (sessionId: string): Promise<void> => {
526
- await logout(sessionId);
527
- };
201
+ const logoutAll = useCallback(async (): Promise<void> => {
202
+ await authManager.logoutAll();
528
203
 
529
- // Refresh sessions method
530
- const refreshSessions = async (): Promise<void> => {
531
- if (!activeSessionId) return;
532
-
533
- try {
534
- const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
535
-
536
- // Update local sessions with server data
537
- const updatedSessions: SecureClientSession[] = serverSessions.map(serverSession => ({
538
- sessionId: serverSession.sessionId,
539
- deviceId: serverSession.deviceId,
540
- expiresAt: new Date().toISOString(), // You might want to get this from server
541
- lastActive: new Date().toISOString()
542
- }));
543
-
544
- setSessions(updatedSessions);
545
- await saveSessionsToStorage(updatedSessions);
546
- } catch (error) {
547
- console.error('Refresh sessions error:', error);
548
- }
549
- };
550
-
551
- // Device management methods
552
- const getDeviceSessions = async (): Promise<any[]> => {
553
- if (!activeSessionId) throw new Error('No active session');
554
-
555
- try {
556
- return await oxyServices.getDeviceSessions(activeSessionId);
557
- } catch (error) {
558
- console.error('Get device sessions error:', error);
559
- throw error;
204
+ // Clear from storage
205
+ if (storage) {
206
+ await storage.removeItem(`${storageKeyPrefix}_active_session_id`);
560
207
  }
561
- };
208
+ }, [authManager, storage, storageKeyPrefix]);
562
209
 
563
- const logoutAllDeviceSessions = async (): Promise<void> => {
564
- if (!activeSessionId) throw new Error('No active session');
210
+ const signUp = useCallback(async (username: string, email: string, password: string): Promise<User> => {
211
+ await authManager.signUp(username, email, password);
565
212
 
566
- try {
567
- await oxyServices.logoutAllDeviceSessions(activeSessionId);
568
-
569
- // Clear all local sessions since we logged out from all devices
570
- setSessions([]);
571
- setActiveSessionId(null);
572
- setUser(null);
573
- setMinimalUser(null);
574
- await clearAllStorage();
575
-
576
- if (onAuthStateChange) {
577
- onAuthStateChange(null);
578
- }
579
- } catch (error) {
580
- console.error('Logout all device sessions error:', error);
581
- throw error;
213
+ // Save session ID to storage after auto-login
214
+ const sessionId = authManager.getActiveSessionId();
215
+ if (sessionId && storage) {
216
+ await storage.setItem(`${storageKeyPrefix}_active_session_id`, sessionId);
582
217
  }
583
- };
584
-
585
- const updateDeviceName = async (deviceName: string): Promise<void> => {
586
- if (!activeSessionId) throw new Error('No active session');
587
218
 
588
- try {
589
- await oxyServices.updateDeviceName(activeSessionId, deviceName);
219
+ return authManager.getCurrentUser();
220
+ }, [authManager, storage, storageKeyPrefix]);
590
221
 
591
- // Update local device info
592
- await DeviceManager.updateDeviceName(deviceName);
593
- } catch (error) {
594
- console.error('Update device name error:', error);
595
- throw error;
596
- }
597
- };
598
-
599
- // Bottom sheet control methods
222
+ // Bottom sheet controls
600
223
  const showBottomSheet = useCallback((screenOrConfig?: string | { screen: string; props?: Record<string, any> }) => {
601
- console.log('showBottomSheet called with:', screenOrConfig);
602
-
603
224
  if (bottomSheetRef?.current) {
604
- console.log('bottomSheetRef is available');
605
-
606
- // First, show the bottom sheet
607
225
  if (bottomSheetRef.current.expand) {
608
- console.log('Expanding bottom sheet');
609
226
  bottomSheetRef.current.expand();
610
227
  } else if (bottomSheetRef.current.present) {
611
- console.log('Presenting bottom sheet');
612
228
  bottomSheetRef.current.present();
613
- } else {
614
- console.warn('No expand or present method available on bottomSheetRef');
615
229
  }
616
230
 
617
- // Then navigate to the specified screen if provided
618
231
  if (screenOrConfig) {
619
- // Add a small delay to ensure the bottom sheet is opened first
620
232
  setTimeout(() => {
621
233
  if (typeof screenOrConfig === 'string') {
622
- // Simple screen name
623
- console.log('Navigating to screen:', screenOrConfig);
624
234
  bottomSheetRef.current?._navigateToScreen?.(screenOrConfig);
625
235
  } else {
626
- // Screen with props
627
- console.log('Navigating to screen with props:', screenOrConfig.screen, screenOrConfig.props);
628
236
  bottomSheetRef.current?._navigateToScreen?.(screenOrConfig.screen, screenOrConfig.props);
629
237
  }
630
238
  }, 100);
631
239
  }
632
- } else {
633
- console.warn('bottomSheetRef is not available');
634
240
  }
635
241
  }, [bottomSheetRef]);
636
242
 
@@ -640,36 +246,22 @@ export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
640
246
  }
641
247
  }, [bottomSheetRef]);
642
248
 
643
- // Compute comprehensive authentication status
644
- // This is the single source of truth for authentication across the entire app
645
- const isAuthenticated = useMemo(() => {
646
- // User is authenticated if:
647
- // 1. We have a full user object loaded, OR
648
- // 2. We have an active session with a valid token
649
- // This covers both the loaded state and the loading-but-authenticated state
650
- return !!user || (!!activeSessionId && !!oxyServices?.getCurrentUserId());
651
- }, [user, activeSessionId, oxyServices]);
652
-
653
- // Context value
654
249
  const contextValue: OxyContextState = {
655
- user,
656
- minimalUser,
657
- sessions,
658
- activeSessionId,
659
- isAuthenticated,
660
- isLoading,
661
- error,
250
+ isAuthenticated: authState.isAuthenticated,
251
+ user: authState.user,
252
+ isLoading: authState.isLoading,
253
+ error: authState.error,
254
+ sessions: authState.sessions,
255
+ activeSessionId: authState.activeSessionId,
662
256
  login,
663
257
  logout,
664
- logoutAll,
665
258
  signUp,
259
+ refreshSessions,
666
260
  switchSession,
667
261
  removeSession,
668
- refreshSessions,
669
- getDeviceSessions,
670
- logoutAllDeviceSessions,
671
- updateDeviceName,
262
+ logoutAll,
672
263
  oxyServices,
264
+ authManager,
673
265
  bottomSheetRef,
674
266
  showBottomSheet,
675
267
  hideBottomSheet,
@@ -682,7 +274,6 @@ export const OxyContextProvider: React.FC<OxyContextProviderProps> = ({
682
274
  );
683
275
  };
684
276
 
685
- // Hook to use the context
686
277
  export const useOxy = (): OxyContextState => {
687
278
  const context = useContext(OxyContext);
688
279
  if (!context) {