@oxyhq/services 5.17.5 → 5.17.8

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 (54) hide show
  1. package/lib/commonjs/crypto/keyManager.js +6 -161
  2. package/lib/commonjs/crypto/keyManager.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +20 -543
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContextBase.js.map +1 -1
  6. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +14 -331
  7. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  8. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +8 -112
  9. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  10. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +2 -27
  11. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  12. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +2 -27
  13. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  14. package/lib/commonjs/ui/hooks/useSessionSocket.js +2 -88
  15. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  16. package/lib/module/crypto/keyManager.js +6 -161
  17. package/lib/module/crypto/keyManager.js.map +1 -1
  18. package/lib/module/ui/context/OxyContext.js +20 -543
  19. package/lib/module/ui/context/OxyContext.js.map +1 -1
  20. package/lib/module/ui/context/OxyContextBase.js.map +1 -1
  21. package/lib/module/ui/context/hooks/useAuthOperations.js +14 -330
  22. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  23. package/lib/module/ui/hooks/mutations/useAccountMutations.js +8 -112
  24. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  25. package/lib/module/ui/hooks/queries/useAccountQueries.js +2 -27
  26. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  27. package/lib/module/ui/hooks/queries/useServicesQueries.js +2 -27
  28. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  29. package/lib/module/ui/hooks/useSessionSocket.js +2 -88
  30. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  31. package/lib/typescript/crypto/keyManager.d.ts +3 -20
  32. package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
  33. package/lib/typescript/crypto/types.d.ts +4 -0
  34. package/lib/typescript/crypto/types.d.ts.map +1 -1
  35. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  36. package/lib/typescript/ui/context/OxyContextBase.d.ts +0 -37
  37. package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
  38. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +1 -20
  39. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  40. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  41. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  42. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  43. package/lib/typescript/ui/hooks/useSessionSocket.d.ts +1 -14
  44. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/crypto/keyManager.ts +4 -170
  47. package/src/crypto/types.ts +4 -0
  48. package/src/ui/context/OxyContext.tsx +17 -588
  49. package/src/ui/context/OxyContextBase.tsx +2 -20
  50. package/src/ui/context/hooks/useAuthOperations.ts +22 -347
  51. package/src/ui/hooks/mutations/useAccountMutations.ts +12 -110
  52. package/src/ui/hooks/queries/useAccountQueries.ts +3 -27
  53. package/src/ui/hooks/queries/useServicesQueries.ts +3 -27
  54. package/src/ui/hooks/useSessionSocket.ts +2 -106
@@ -17,7 +17,6 @@ Object.defineProperty(exports, "useOxy", {
17
17
  }
18
18
  });
19
19
  var _react = require("react");
20
- var _reactNative = require("react-native");
21
20
  var _core = require("../../core");
22
21
  var _sonner = require("../../lib/sonner");
23
22
  var _authStore = require("../stores/authStore");
@@ -34,7 +33,6 @@ var _bottomSheetManager = require("../navigation/bottomSheetManager");
34
33
  var _reactQuery = require("@tanstack/react-query");
35
34
  var _queryClient = require("../hooks/queryClient");
36
35
  var _queryKeys = require("../hooks/queries/queryKeys");
37
- var _crypto = require("../../crypto");
38
36
  var _i18n = require("../../i18n");
39
37
  var _avatarUtils = require("../utils/avatarUtils");
40
38
  var _accountStore = require("../stores/accountStore");
@@ -97,24 +95,14 @@ const OxyProvider = ({
97
95
  error,
98
96
  loginSuccess,
99
97
  loginFailure,
100
- logoutStore,
101
- // Identity sync state and actions
102
- isIdentitySyncedStore,
103
- isSyncing,
104
- setIdentitySynced,
105
- setSyncing
98
+ logoutStore
106
99
  } = (0, _authStore.useAuthStore)((0, _shallow.useShallow)(state => ({
107
100
  isAuthenticated: state.isAuthenticated,
108
101
  isLoading: state.isLoading,
109
102
  error: state.error,
110
103
  loginSuccess: state.loginSuccess,
111
104
  loginFailure: state.loginFailure,
112
- logoutStore: state.logout,
113
- // Identity sync state and actions
114
- isIdentitySyncedStore: state.isIdentitySynced,
115
- isSyncing: state.isSyncing,
116
- setIdentitySynced: state.setIdentitySynced,
117
- setSyncing: state.setSyncing
105
+ logoutStore: state.logout
118
106
  })));
119
107
  const [tokenReady, setTokenReady] = (0, _react.useState)(true);
120
108
  const initializedRef = (0, _react.useRef)(false);
@@ -137,45 +125,6 @@ const OxyProvider = ({
137
125
  logger
138
126
  });
139
127
 
140
- // Identity integrity check and auto-restore on startup
141
- // Skip on web platform - identity storage is only available on native platforms
142
- (0, _react.useEffect)(() => {
143
- if (!storage || !isStorageReady) return;
144
- if (_reactNative.Platform.OS === 'web') return; // Identity operations are native-only
145
-
146
- const checkAndRestoreIdentity = async () => {
147
- try {
148
- // Check if identity exists and verify integrity
149
- const hasIdentity = await _crypto.KeyManager.hasIdentity();
150
- if (hasIdentity) {
151
- const isValid = await _crypto.KeyManager.verifyIdentityIntegrity();
152
- if (!isValid) {
153
- // Try to restore from backup
154
- const restored = await _crypto.KeyManager.restoreIdentityFromBackup();
155
- if (__DEV__) {
156
- logger(restored ? 'Identity restored from backup successfully' : 'Identity integrity check failed - user may need to restore from backup file');
157
- }
158
- } else {
159
- // Identity is valid - ensure backup is up to date
160
- await _crypto.KeyManager.backupIdentity();
161
- }
162
- } else {
163
- // No identity - try to restore from backup
164
- const restored = await _crypto.KeyManager.restoreIdentityFromBackup();
165
- if (restored && __DEV__) {
166
- logger('Identity restored from backup on startup');
167
- }
168
- }
169
- } catch (error) {
170
- if (__DEV__) {
171
- logger('Error during identity integrity check', error);
172
- }
173
- // Don't block app startup - user can recover with backup file
174
- }
175
- };
176
- checkAndRestoreIdentity();
177
- }, [storage, isStorageReady, logger]);
178
-
179
128
  // Offline queuing is now handled by TanStack Query mutations
180
129
  // No need for custom offline queue
181
130
 
@@ -243,15 +192,9 @@ const OxyProvider = ({
243
192
  });
244
193
  const user = userData ?? null;
245
194
  const {
246
- createIdentity,
247
- importIdentity: importIdentityBase,
248
195
  signIn,
249
196
  logout,
250
- logoutAll,
251
- hasIdentity,
252
- getPublicKey,
253
- isIdentitySynced,
254
- syncIdentity: syncIdentityBase
197
+ logoutAll
255
198
  } = (0, _useAuthOperations.useAuthOperations)({
256
199
  oxyServices,
257
200
  storage,
@@ -269,29 +212,10 @@ const OxyProvider = ({
269
212
  loginFailure,
270
213
  logoutStore,
271
214
  setAuthState,
272
- setIdentitySynced,
273
- setSyncing,
274
215
  logger
275
216
  });
276
217
 
277
- // syncIdentity - TanStack Query handles offline mutations automatically
278
- const syncIdentity = (0, _react.useCallback)(() => syncIdentityBase(), [syncIdentityBase]);
279
-
280
- // Wrapper for importIdentity to handle legacy calls gracefully
281
- const importIdentity = (0, _react.useCallback)(async (backupData, password) => {
282
- // Handle legacy calls with single string argument (old recovery phrase signature)
283
- if (typeof backupData === 'string') {
284
- throw new Error('Recovery phrase import is no longer supported. Please use backup file import or QR code transfer instead.');
285
- }
286
-
287
- // Validate that password is provided
288
- if (!password || typeof password !== 'string') {
289
- throw new Error('Password is required for backup file import.');
290
- }
291
- return importIdentityBase(backupData, password);
292
- }, [importIdentityBase]);
293
-
294
- // Clear all account data when identity is lost (for accounts app)
218
+ // Clear all account data when logging out (for accounts app)
295
219
  // In accounts app, identity = account, so losing identity means losing everything
296
220
  const clearAllAccountData = (0, _react.useCallback)(async () => {
297
221
  // Clear TanStack Query cache (in-memory)
@@ -309,19 +233,6 @@ const OxyProvider = ({
309
233
  // Clear session state (sessions, activeSessionId, storage)
310
234
  await clearSessionState();
311
235
 
312
- // Clear identity sync state from storage
313
- if (storage) {
314
- try {
315
- await storage.removeItem('oxy_identity_synced');
316
- } catch (error) {
317
- logger('Failed to clear identity sync state', error);
318
- }
319
- }
320
-
321
- // Reset auth store identity sync state
322
- _authStore.useAuthStore.getState().setIdentitySynced(false);
323
- _authStore.useAuthStore.getState().setSyncing(false);
324
-
325
236
  // Reset account store
326
237
  _accountStore.useAccountStore.getState().reset();
327
238
 
@@ -329,162 +240,50 @@ const OxyProvider = ({
329
240
  oxyServices.clearCache();
330
241
  }, [queryClient, storage, clearSessionState, logger, oxyServices]);
331
242
 
332
- // Transfer code management functions (must be defined before deleteIdentityAndClearAccount)
333
- const getAllPendingTransfers = (0, _react.useCallback)(() => {
334
- const pending = [];
335
- transferCodesRef.current.forEach((data, transferId) => {
336
- if (data.state === 'pending') {
337
- pending.push({
338
- transferId,
339
- data
340
- });
341
- }
342
- });
343
- return pending;
344
- }, []);
345
- const getActiveTransferId = (0, _react.useCallback)(() => {
346
- return activeTransferIdRef.current;
347
- }, []);
348
-
349
- // Delete identity and clear all account data
350
- // In accounts app, deleting identity means losing the account completely
351
- const deleteIdentityAndClearAccount = (0, _react.useCallback)(async (skipBackup = false, force = false, userConfirmed = false) => {
352
- // CRITICAL: Check for active transfers before deletion (unless force is true)
353
- // This prevents accidental identity loss during transfer
354
- if (!force) {
355
- const pendingTransfers = getAllPendingTransfers();
356
- if (pendingTransfers.length > 0) {
357
- const activeTransferId = getActiveTransferId();
358
- const hasActiveTransfer = activeTransferId && pendingTransfers.some(t => t.transferId === activeTransferId);
359
- if (hasActiveTransfer) {
360
- throw new Error('Cannot delete identity: An active identity transfer is in progress. ' + 'Please wait for the transfer to complete or cancel it first. ' + 'If you proceed, you may lose access to your identity permanently.');
361
- }
362
- }
363
- }
364
-
365
- // First, clear all account data
366
- await clearAllAccountData();
367
-
368
- // Then delete the identity keys
369
- await _crypto.KeyManager.deleteIdentity(skipBackup, force, userConfirmed);
370
- }, [clearAllAccountData, getAllPendingTransfers, getActiveTransferId]);
371
-
372
- // Network reconnect sync - TanStack Query automatically retries mutations on reconnect
373
- // We only need to sync identity if it's not synced
243
+ // Network reconnect - TanStack Query automatically retries mutations on reconnect
244
+ // Network reconnect - TanStack Query automatically retries mutations on reconnect
374
245
  (0, _react.useEffect)(() => {
375
246
  if (!storage) return;
376
247
  let wasOffline = false;
377
248
  let checkTimeout = null;
378
- let lastReconnectionLog = 0;
379
- const RECONNECTION_LOG_DEBOUNCE_MS = 5000; // 5 seconds
380
-
381
- // Circuit breaker and exponential backoff state
382
- const stateRef = {
383
- consecutiveFailures: 0,
384
- currentInterval: 10000,
385
- // Start with 10 seconds
386
- baseInterval: 10000,
387
- // Base interval in milliseconds
388
- maxInterval: 60000,
389
- // Maximum interval (60 seconds)
390
- maxFailures: 5 // Circuit breaker threshold
391
- };
392
249
  const scheduleNextCheck = () => {
393
250
  if (checkTimeout) {
394
251
  clearTimeout(checkTimeout);
395
252
  }
396
253
  checkTimeout = setTimeout(() => {
397
- checkNetworkAndSync();
398
- }, stateRef.currentInterval);
254
+ checkNetworkStatus();
255
+ }, 30000); // Check every 30 seconds
399
256
  };
400
- const checkNetworkAndSync = async () => {
257
+ const checkNetworkStatus = async () => {
401
258
  try {
402
259
  // Try a lightweight health check to see if we're online
403
- await oxyServices.healthCheck().catch(() => {
404
- wasOffline = true;
405
- throw new Error('Health check failed');
406
- });
407
-
408
- // Health check succeeded - reset circuit breaker and backoff
409
- if (stateRef.consecutiveFailures > 0) {
410
- stateRef.consecutiveFailures = 0;
411
- stateRef.currentInterval = stateRef.baseInterval;
412
- }
260
+ await oxyServices.healthCheck();
413
261
 
414
- // If we were offline and now we're online, sync identity if needed
262
+ // If we were offline and now we're online
415
263
  if (wasOffline) {
416
- const now = Date.now();
417
- const timeSinceLastLog = now - lastReconnectionLog;
418
- if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
419
- logger('Network reconnected, checking identity sync...');
420
- lastReconnectionLog = now;
421
-
422
- // Sync identity first (if not synced)
423
- try {
424
- const hasIdentityValue = await hasIdentity();
425
- if (hasIdentityValue) {
426
- // Check sync status directly - sync if not explicitly 'true'
427
- // undefined = not synced yet, 'false' = explicitly not synced, 'true' = synced
428
- const syncStatus = await storage.getItem('oxy_identity_synced');
429
- if (syncStatus !== 'true') {
430
- await syncIdentity();
431
- }
432
- }
433
- } catch (syncError) {
434
- // Skip sync silently if username is required (expected when offline onboarding)
435
- if (syncError?.code === 'USERNAME_REQUIRED' || syncError?.message === 'USERNAME_REQUIRED') {
436
- if (__DEV__) {
437
- _loggerUtils.logger.debug('Sync skipped - username required', {
438
- component: 'OxyContext',
439
- method: 'checkNetworkAndSync'
440
- }, syncError);
441
- }
442
- // Don't log or show error - username will be set later
443
- } else if (!(0, _errorHandlers.isTimeoutOrNetworkError)(syncError)) {
444
- // Only log unexpected errors - timeouts/network issues are expected when offline
445
- logger('Error syncing identity on reconnect', syncError);
446
- } else if (__DEV__) {
447
- _loggerUtils.logger.debug('Identity sync timeout (expected when offline)', {
448
- component: 'OxyContext',
449
- method: 'checkNetworkAndSync'
450
- }, syncError);
451
- }
452
- }
453
- }
454
-
264
+ logger('Network reconnected');
455
265
  // TanStack Query will automatically retry pending mutations
456
- // Reset flag immediately after processing (whether logged or not)
457
266
  wasOffline = false;
458
267
  }
459
268
  } catch (error) {
460
269
  // Network check failed - we're offline
461
- wasOffline = true;
462
-
463
- // Increment failure count and apply exponential backoff
464
- stateRef.consecutiveFailures++;
465
-
466
- // Calculate new interval with exponential backoff, capped at maxInterval
467
- const backoffMultiplier = Math.min(Math.pow(2, stateRef.consecutiveFailures - 1), stateRef.maxInterval / stateRef.baseInterval);
468
- stateRef.currentInterval = Math.min(stateRef.baseInterval * backoffMultiplier, stateRef.maxInterval);
469
-
470
- // If we hit the circuit breaker threshold, use max interval
471
- if (stateRef.consecutiveFailures >= stateRef.maxFailures) {
472
- stateRef.currentInterval = stateRef.maxInterval;
270
+ if (!wasOffline && __DEV__) {
271
+ logger('Network appears offline');
473
272
  }
273
+ wasOffline = true;
474
274
  } finally {
475
- // Always schedule next check (will use updated interval)
476
275
  scheduleNextCheck();
477
276
  }
478
277
  };
479
278
 
480
279
  // Check immediately
481
- checkNetworkAndSync();
280
+ checkNetworkStatus();
482
281
  return () => {
483
282
  if (checkTimeout) {
484
283
  clearTimeout(checkTimeout);
485
284
  }
486
285
  };
487
- }, [oxyServices, storage, syncIdentity, logger]);
286
+ }, [oxyServices, storage, logger]);
488
287
  const {
489
288
  getDeviceSessions,
490
289
  logoutAllDeviceSessions,
@@ -593,171 +392,6 @@ const OxyProvider = ({
593
392
 
594
393
  // Get userId from JWT token (MongoDB ObjectId) for socket room matching
595
394
  const userId = oxyServices.getCurrentUserId();
596
-
597
- // Transfer code storage interface
598
-
599
- // Store transfer codes in memory for verification (also persisted to storage)
600
- // Map: transferId -> TransferCodeData
601
- const transferCodesRef = (0, _react.useRef)(new Map());
602
- const activeTransferIdRef = (0, _react.useRef)(null);
603
- const TRANSFER_CODES_STORAGE_KEY = `${storageKeyPrefix}_transfer_codes`;
604
- const ACTIVE_TRANSFER_STORAGE_KEY = `${storageKeyPrefix}_active_transfer_id`;
605
-
606
- // Clear stale transfer codes on startup
607
- // Transfers are ephemeral and should not persist across app restarts
608
- (0, _react.useEffect)(() => {
609
- if (!storage || !isStorageReady) return;
610
- const clearStaleTransfers = async () => {
611
- try {
612
- // Clear any stored transfer codes - they're only valid during active transfer sessions
613
- await storage.removeItem(TRANSFER_CODES_STORAGE_KEY);
614
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
615
- if (__DEV__) {
616
- logger('Cleared stale transfer codes on startup');
617
- }
618
- } catch (error) {
619
- if (__DEV__) {
620
- logger('Failed to clear stale transfer codes', error);
621
- }
622
- }
623
- };
624
- clearStaleTransfers();
625
- }, [storage, isStorageReady, logger, storageKeyPrefix]);
626
-
627
- // Persist transfer codes to storage whenever they change
628
- const persistTransferCodes = (0, _react.useCallback)(async () => {
629
- if (!storage) return;
630
- try {
631
- const codesToStore = {};
632
- transferCodesRef.current.forEach((value, key) => {
633
- codesToStore[key] = value;
634
- });
635
- await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(codesToStore));
636
- } catch (error) {
637
- if (__DEV__) {
638
- logger('Failed to persist transfer codes', error);
639
- }
640
- }
641
- }, [storage, logger]);
642
-
643
- // Cleanup old transfer codes (older than 15 minutes)
644
- (0, _react.useEffect)(() => {
645
- const cleanup = setInterval(async () => {
646
- const now = Date.now();
647
- const fifteenMinutes = 15 * 60 * 1000;
648
- let needsPersist = false;
649
- transferCodesRef.current.forEach((value, key) => {
650
- if (now - value.timestamp > fifteenMinutes) {
651
- transferCodesRef.current.delete(key);
652
- needsPersist = true;
653
- if (__DEV__) {
654
- logger('Cleaned up expired transfer code', {
655
- transferId: key
656
- });
657
- }
658
- }
659
- });
660
-
661
- // Clear active transfer if it was deleted
662
- if (activeTransferIdRef.current && !transferCodesRef.current.has(activeTransferIdRef.current)) {
663
- activeTransferIdRef.current = null;
664
- if (storage) {
665
- try {
666
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
667
- } catch (error) {
668
- // Ignore storage errors
669
- }
670
- }
671
- }
672
- if (needsPersist) {
673
- await persistTransferCodes();
674
- }
675
- }, 60000); // Check every minute
676
-
677
- return () => clearInterval(cleanup);
678
- }, [logger, persistTransferCodes, storage]);
679
-
680
- // Transfer code management functions
681
- const storeTransferCode = (0, _react.useCallback)(async (transferId, code, sourceDeviceId, publicKey) => {
682
- const transferData = {
683
- code,
684
- sourceDeviceId,
685
- publicKey,
686
- timestamp: Date.now(),
687
- state: 'pending'
688
- };
689
- transferCodesRef.current.set(transferId, transferData);
690
- activeTransferIdRef.current = transferId;
691
-
692
- // Persist to storage
693
- await persistTransferCodes();
694
- if (storage) {
695
- try {
696
- await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, transferId);
697
- } catch (error) {
698
- if (__DEV__) {
699
- logger('Failed to persist active transfer ID', error);
700
- }
701
- }
702
- }
703
- if (__DEV__) {
704
- logger('Stored transfer code', {
705
- transferId,
706
- sourceDeviceId,
707
- publicKey: publicKey.substring(0, 16) + '...'
708
- });
709
- }
710
- }, [logger, persistTransferCodes, storage]);
711
- const getTransferCode = (0, _react.useCallback)(transferId => {
712
- return transferCodesRef.current.get(transferId) || null;
713
- }, []);
714
- const updateTransferState = (0, _react.useCallback)(async (transferId, state) => {
715
- const transferData = transferCodesRef.current.get(transferId);
716
- if (transferData) {
717
- transferData.state = state;
718
- transferCodesRef.current.set(transferId, transferData);
719
-
720
- // Clear active transfer if completed or failed
721
- if (state === 'completed' || state === 'failed') {
722
- if (activeTransferIdRef.current === transferId) {
723
- activeTransferIdRef.current = null;
724
- if (storage) {
725
- try {
726
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
727
- } catch (error) {
728
- // Ignore storage errors
729
- }
730
- }
731
- }
732
- }
733
- await persistTransferCodes();
734
- if (__DEV__) {
735
- logger('Updated transfer state', {
736
- transferId,
737
- state
738
- });
739
- }
740
- }
741
- }, [logger, persistTransferCodes, storage]);
742
- const clearTransferCode = (0, _react.useCallback)(async transferId => {
743
- transferCodesRef.current.delete(transferId);
744
- if (activeTransferIdRef.current === transferId) {
745
- activeTransferIdRef.current = null;
746
- if (storage) {
747
- try {
748
- await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
749
- } catch (error) {
750
- // Ignore storage errors
751
- }
752
- }
753
- }
754
- await persistTransferCodes();
755
- if (__DEV__) {
756
- logger('Cleared transfer code', {
757
- transferId
758
- });
759
- }
760
- }, [logger, persistTransferCodes, storage]);
761
395
  const refreshSessionsWithUser = (0, _react.useCallback)(() => refreshSessions(userId || undefined), [refreshSessions, userId]);
762
396
  const handleSessionRemoved = (0, _react.useCallback)(sessionId => {
763
397
  trackRemovedSession(sessionId);
@@ -766,143 +400,6 @@ const OxyProvider = ({
766
400
  _sonner.toast.info('You have been signed out remotely.');
767
401
  logout().catch(remoteError => logger('Failed to process remote sign out', remoteError));
768
402
  }, [logger, logout]);
769
- const handleIdentityTransferComplete = (0, _react.useCallback)(async data => {
770
- try {
771
- logger('Received identity transfer complete notification', {
772
- transferId: data.transferId,
773
- sourceDeviceId: data.sourceDeviceId,
774
- currentDeviceId,
775
- hasActiveSession: activeSessionId !== null,
776
- publicKey: data.publicKey.substring(0, 16) + '...'
777
- });
778
- const storedTransfer = getTransferCode(data.transferId);
779
- if (!storedTransfer) {
780
- logger('Transfer code not found for transferId', {
781
- transferId: data.transferId
782
- });
783
- _sonner.toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
784
- return;
785
- }
786
-
787
- // Verify publicKey matches first (most important check)
788
- const publicKeyMatches = data.publicKey === storedTransfer.publicKey;
789
- if (!publicKeyMatches) {
790
- logger('Public key mismatch for transfer', {
791
- transferId: data.transferId,
792
- receivedPublicKey: data.publicKey.substring(0, 16) + '...',
793
- storedPublicKey: storedTransfer.publicKey.substring(0, 16) + '...'
794
- });
795
- _sonner.toast.error('Transfer verification failed: Public key mismatch. Identity will not be deleted.');
796
- return;
797
- }
798
-
799
- // Verify deviceId matches - very lenient since publicKey is the critical check
800
- // If publicKey matches, we allow deletion even if deviceId doesn't match exactly
801
- // This handles cases where deviceId might not be available or slightly different
802
- const deviceIdMatches =
803
- // Exact match
804
- data.sourceDeviceId && data.sourceDeviceId === currentDeviceId ||
805
- // Stored sourceDeviceId matches current deviceId
806
- storedTransfer.sourceDeviceId && storedTransfer.sourceDeviceId === currentDeviceId;
807
-
808
- // If publicKey matches, we're very lenient with deviceId - only warn but don't block
809
- if (!deviceIdMatches && publicKeyMatches) {
810
- logger('Device ID mismatch for transfer, but publicKey matches - proceeding with deletion', {
811
- transferId: data.transferId,
812
- receivedDeviceId: data.sourceDeviceId,
813
- storedDeviceId: storedTransfer.sourceDeviceId,
814
- currentDeviceId,
815
- hasActiveSession: activeSessionId !== null
816
- });
817
- // Proceed with deletion - publicKey match is the critical verification
818
- } else if (!deviceIdMatches && !publicKeyMatches) {
819
- // Both don't match - this is suspicious, block deletion
820
- logger('Device ID and publicKey mismatch for transfer', {
821
- transferId: data.transferId,
822
- receivedDeviceId: data.sourceDeviceId,
823
- currentDeviceId
824
- });
825
- _sonner.toast.error('Transfer verification failed: Device and key mismatch. Identity will not be deleted.');
826
- return;
827
- }
828
-
829
- // Verify transfer code matches (if provided)
830
- // Transfer code is optional - if not provided, we still proceed if publicKey matches
831
- if (data.transferCode) {
832
- const codeMatches = data.transferCode.toUpperCase() === storedTransfer.code.toUpperCase();
833
- if (!codeMatches) {
834
- logger('Transfer code mismatch, but publicKey matches - proceeding with deletion', {
835
- transferId: data.transferId,
836
- receivedCode: data.transferCode,
837
- storedCode: storedTransfer.code.substring(0, 2) + '****'
838
- });
839
- // Don't block - publicKey match is sufficient, code mismatch might be due to user error
840
- // Log warning but proceed
841
- }
842
- }
843
-
844
- // Check if transfer is too old (safety timeout - 10 minutes)
845
- const transferAge = Date.now() - storedTransfer.timestamp;
846
- const tenMinutes = 10 * 60 * 1000;
847
- if (transferAge > tenMinutes) {
848
- logger('Transfer confirmation received too late', {
849
- transferId: data.transferId,
850
- age: transferAge,
851
- ageMinutes: Math.round(transferAge / 60000)
852
- });
853
- _sonner.toast.error('Transfer verification failed: Confirmation received too late. Identity will not be deleted.');
854
- clearTransferCode(data.transferId);
855
- return;
856
- }
857
-
858
- // NOTE: Target device verification already happened server-side when notifyTransferComplete was called
859
- // The server verified that the target device is authenticated and has the matching public key
860
- // Additional client-side verification is not necessary and would require source device authentication
861
- // which may not be available. The existing checks (public key match, transfer code, device ID) are sufficient.
862
-
863
- logger('All transfer verifications passed, deleting identity from source device', {
864
- transferId: data.transferId,
865
- sourceDeviceId: data.sourceDeviceId,
866
- publicKey: data.publicKey.substring(0, 16) + '...'
867
- });
868
- try {
869
- // Verify identity still exists before deletion (safety check)
870
- const identityStillExists = await _crypto.KeyManager.hasIdentity();
871
- if (!identityStillExists) {
872
- logger('Identity already deleted - skipping deletion', {
873
- transferId: data.transferId
874
- });
875
- await updateTransferState(data.transferId, 'completed');
876
- await clearTransferCode(data.transferId);
877
- return;
878
- }
879
- await deleteIdentityAndClearAccount(false, false, true);
880
-
881
- // Verify identity was actually deleted
882
- const identityDeleted = !(await _crypto.KeyManager.hasIdentity());
883
- if (!identityDeleted) {
884
- logger('Identity deletion failed - identity still exists', {
885
- transferId: data.transferId
886
- });
887
- await updateTransferState(data.transferId, 'failed');
888
- throw new Error('Identity deletion failed - identity still exists');
889
- }
890
- await updateTransferState(data.transferId, 'completed');
891
- await clearTransferCode(data.transferId);
892
- logger('Identity successfully deleted and transfer code cleared', {
893
- transferId: data.transferId
894
- });
895
- _sonner.toast.success('Identity successfully transferred and removed from this device');
896
- } catch (deleteError) {
897
- logger('Error during identity deletion', deleteError);
898
- await updateTransferState(data.transferId, 'failed');
899
- throw deleteError;
900
- }
901
- } catch (error) {
902
- logger('Failed to delete identity after transfer', error);
903
- _sonner.toast.error(error?.message || 'Failed to remove identity from this device. Please try again manually from Security Settings.');
904
- }
905
- }, [deleteIdentityAndClearAccount, logger, getTransferCode, clearTransferCode, updateTransferState, currentDeviceId, activeSessionId, oxyServices]);
906
403
  (0, _useSessionSocket.useSessionSocket)({
907
404
  userId,
908
405
  activeSessionId,
@@ -912,10 +409,8 @@ const OxyProvider = ({
912
409
  clearSessionState,
913
410
  baseURL: oxyServices.getBaseURL(),
914
411
  getAccessToken: () => oxyServices.getAccessToken(),
915
- getTransferCode: getTransferCode,
916
412
  onRemoteSignOut: handleRemoteSignOut,
917
- onSessionRemoved: handleSessionRemoved,
918
- onIdentityTransferComplete: handleIdentityTransferComplete
413
+ onSessionRemoved: handleSessionRemoved
919
414
  });
920
415
  const switchSessionForContext = (0, _react.useCallback)(async sessionId => {
921
416
  await switchSession(sessionId);
@@ -949,7 +444,6 @@ const OxyProvider = ({
949
444
  await (0, _avatarUtils.updateProfileWithAvatar)({
950
445
  avatar: file.id
951
446
  }, oxyServices, activeSessionId, queryClient, {
952
- syncIdentity,
953
447
  deviceId: currentDeviceId || undefined
954
448
  });
955
449
  _sonner.toast.success((0, _i18n.translate)(currentLanguage, 'editProfile.toasts.avatarUpdated') || 'Avatar updated');
@@ -959,7 +453,7 @@ const OxyProvider = ({
959
453
  }
960
454
  }
961
455
  });
962
- }, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient, syncIdentity]);
456
+ }, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient]);
963
457
  const contextValue = (0, _react.useMemo)(() => ({
964
458
  user,
965
459
  sessions,
@@ -974,24 +468,7 @@ const OxyProvider = ({
974
468
  currentLanguageMetadata,
975
469
  currentLanguageName,
976
470
  currentNativeLanguageName,
977
- createIdentity,
978
- importIdentity,
979
471
  signIn,
980
- hasIdentity,
981
- getPublicKey,
982
- isIdentitySynced,
983
- syncIdentity,
984
- deleteIdentityAndClearAccount,
985
- storeTransferCode,
986
- getTransferCode,
987
- clearTransferCode,
988
- getAllPendingTransfers,
989
- getActiveTransferId,
990
- updateTransferState,
991
- identitySyncState: {
992
- isSynced: isIdentitySyncedStore ?? true,
993
- isSyncing: isSyncing ?? false
994
- },
995
472
  logout,
996
473
  logoutAll,
997
474
  switchSession: switchSessionForContext,
@@ -1007,7 +484,7 @@ const OxyProvider = ({
1007
484
  useFollow: useFollowHook,
1008
485
  showBottomSheet: showBottomSheetForContext,
1009
486
  openAvatarPicker
1010
- }), [activeSessionId, currentDeviceId, createIdentity, importIdentity, signIn, hasIdentity, getPublicKey, isIdentitySynced, syncIdentity, deleteIdentityAndClearAccount, storeTransferCode, getTransferCode, clearTransferCode, getAllPendingTransfers, getActiveTransferId, updateTransferState, isIdentitySyncedStore, isSyncing, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions, isAuthenticated, isLoading, logout, logoutAll, logoutAllDeviceSessions, oxyServices, refreshSessionsWithUser, sessions, setLanguage, switchSessionForContext, tokenReady, isStorageReady, updateDeviceName, clearAllAccountData, useFollowHook, user, showBottomSheetForContext, openAvatarPicker]);
487
+ }), [activeSessionId, currentDeviceId, signIn, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions, isAuthenticated, isLoading, logout, logoutAll, logoutAllDeviceSessions, oxyServices, refreshSessionsWithUser, sessions, setLanguage, switchSessionForContext, tokenReady, isStorageReady, updateDeviceName, clearAllAccountData, useFollowHook, user, showBottomSheetForContext, openAvatarPicker]);
1011
488
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_OxyContextBase.OxyContext.Provider, {
1012
489
  value: contextValue,
1013
490
  children: children