@oxyhq/services 5.16.40 → 5.16.41
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/adapters/expo/crypto.js +56 -0
- package/lib/commonjs/adapters/expo/crypto.js.map +1 -0
- package/lib/commonjs/adapters/expo/fetch.js +30 -0
- package/lib/commonjs/adapters/expo/fetch.js.map +1 -0
- package/lib/commonjs/adapters/expo/index.js +48 -0
- package/lib/commonjs/adapters/expo/index.js.map +1 -0
- package/lib/commonjs/adapters/expo/storage.js +201 -0
- package/lib/commonjs/adapters/expo/storage.js.map +1 -0
- package/lib/commonjs/adapters/index.js +41 -0
- package/lib/commonjs/adapters/index.js.map +1 -0
- package/lib/commonjs/adapters/node/crypto.js +40 -0
- package/lib/commonjs/adapters/node/crypto.js.map +1 -0
- package/lib/commonjs/adapters/node/fetch.js +62 -0
- package/lib/commonjs/adapters/node/fetch.js.map +1 -0
- package/lib/commonjs/adapters/node/index.js +34 -0
- package/lib/commonjs/adapters/node/index.js.map +1 -0
- package/lib/commonjs/adapters/node/storage.js +163 -0
- package/lib/commonjs/adapters/node/storage.js.map +1 -0
- package/lib/commonjs/core/identity-session/DeviceManager.js +237 -0
- package/lib/commonjs/core/identity-session/DeviceManager.js.map +1 -0
- package/lib/commonjs/core/identity-session/INTEGRATION_GUIDE.md +287 -0
- package/lib/commonjs/core/identity-session/IdentityManager.js +400 -0
- package/lib/commonjs/core/identity-session/IdentityManager.js.map +1 -0
- package/lib/commonjs/core/identity-session/IdentitySessionCore.js +394 -0
- package/lib/commonjs/core/identity-session/IdentitySessionCore.js.map +1 -0
- package/lib/commonjs/core/identity-session/RefreshManager.js +137 -0
- package/lib/commonjs/core/identity-session/RefreshManager.js.map +1 -0
- package/lib/commonjs/core/identity-session/SessionManager.js +427 -0
- package/lib/commonjs/core/identity-session/SessionManager.js.map +1 -0
- package/lib/commonjs/core/identity-session/createIdentitySessionCore.js +24 -0
- package/lib/commonjs/core/identity-session/createIdentitySessionCore.js.map +1 -0
- package/lib/commonjs/core/identity-session/errors.js +176 -0
- package/lib/commonjs/core/identity-session/errors.js.map +1 -0
- package/lib/commonjs/core/identity-session/index.js +80 -0
- package/lib/commonjs/core/identity-session/index.js.map +1 -0
- package/lib/commonjs/core/identity-session/types.js +2 -0
- package/lib/commonjs/core/identity-session/types.js.map +1 -0
- package/lib/commonjs/core/index.js +2 -21
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +58 -8
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +7 -0
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +434 -820
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +52 -0
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -0
- package/lib/commonjs/ui/hooks/useIdentityTransfer.js +125 -0
- package/lib/commonjs/ui/hooks/useIdentityTransfer.js.map +1 -0
- package/lib/commonjs/ui/hooks/useTransferCodesPersistence.js +81 -0
- package/lib/commonjs/ui/hooks/useTransferCodesPersistence.js.map +1 -0
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +7 -2
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +12 -5
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +2 -2
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +6 -6
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +7 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/index.js +0 -7
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/sessionUtils.js +8 -1
- package/lib/commonjs/utils/sessionUtils.js.map +1 -1
- package/lib/module/adapters/expo/crypto.js +51 -0
- package/lib/module/adapters/expo/crypto.js.map +1 -0
- package/lib/module/adapters/expo/fetch.js +26 -0
- package/lib/module/adapters/expo/fetch.js.map +1 -0
- package/lib/module/adapters/expo/index.js +45 -0
- package/lib/module/adapters/expo/index.js.map +1 -0
- package/lib/module/adapters/expo/storage.js +198 -0
- package/lib/module/adapters/expo/storage.js.map +1 -0
- package/lib/module/adapters/index.js +38 -0
- package/lib/module/adapters/index.js.map +1 -0
- package/lib/module/adapters/node/crypto.js +36 -0
- package/lib/module/adapters/node/crypto.js.map +1 -0
- package/lib/module/adapters/node/fetch.js +57 -0
- package/lib/module/adapters/node/fetch.js.map +1 -0
- package/lib/module/adapters/node/index.js +31 -0
- package/lib/module/adapters/node/index.js.map +1 -0
- package/lib/module/adapters/node/storage.js +159 -0
- package/lib/module/adapters/node/storage.js.map +1 -0
- package/lib/module/core/identity-session/DeviceManager.js +232 -0
- package/lib/module/core/identity-session/DeviceManager.js.map +1 -0
- package/lib/module/core/identity-session/INTEGRATION_GUIDE.md +287 -0
- package/lib/module/core/identity-session/IdentityManager.js +395 -0
- package/lib/module/core/identity-session/IdentityManager.js.map +1 -0
- package/lib/module/core/identity-session/IdentitySessionCore.js +390 -0
- package/lib/module/core/identity-session/IdentitySessionCore.js.map +1 -0
- package/lib/module/core/identity-session/RefreshManager.js +132 -0
- package/lib/module/core/identity-session/RefreshManager.js.map +1 -0
- package/lib/module/core/identity-session/SessionManager.js +422 -0
- package/lib/module/core/identity-session/SessionManager.js.map +1 -0
- package/lib/module/core/identity-session/createIdentitySessionCore.js +21 -0
- package/lib/module/core/identity-session/createIdentitySessionCore.js.map +1 -0
- package/lib/module/core/identity-session/errors.js +170 -0
- package/lib/module/core/identity-session/errors.js.map +1 -0
- package/lib/module/core/identity-session/index.js +17 -0
- package/lib/module/core/identity-session/index.js.map +1 -0
- package/lib/module/core/identity-session/types.js +2 -0
- package/lib/module/core/identity-session/types.js.map +1 -0
- package/lib/module/core/index.js +2 -3
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +12 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +7 -0
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +436 -822
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useAvatarPicker.js +48 -0
- package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -0
- package/lib/module/ui/hooks/useIdentityTransfer.js +121 -0
- package/lib/module/ui/hooks/useIdentityTransfer.js.map +1 -0
- package/lib/module/ui/hooks/useTransferCodesPersistence.js +77 -0
- package/lib/module/ui/hooks/useTransferCodesPersistence.js.map +1 -0
- package/lib/module/ui/screens/AccountCenterScreen.js +7 -2
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +12 -5
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +2 -2
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +6 -6
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +7 -1
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/index.js +2 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/sessionUtils.js +8 -1
- package/lib/module/utils/sessionUtils.js.map +1 -1
- package/lib/typescript/adapters/expo/crypto.d.ts +17 -0
- package/lib/typescript/adapters/expo/crypto.d.ts.map +1 -0
- package/lib/typescript/adapters/expo/fetch.d.ts +16 -0
- package/lib/typescript/adapters/expo/fetch.d.ts.map +1 -0
- package/lib/typescript/adapters/expo/index.d.ts +23 -0
- package/lib/typescript/adapters/expo/index.d.ts.map +1 -0
- package/lib/typescript/adapters/expo/storage.d.ts +23 -0
- package/lib/typescript/adapters/expo/storage.d.ts.map +1 -0
- package/lib/typescript/adapters/index.d.ts +13 -0
- package/lib/typescript/adapters/index.d.ts.map +1 -0
- package/lib/typescript/adapters/node/crypto.d.ts +17 -0
- package/lib/typescript/adapters/node/crypto.d.ts.map +1 -0
- package/lib/typescript/adapters/node/fetch.d.ts +16 -0
- package/lib/typescript/adapters/node/fetch.d.ts.map +1 -0
- package/lib/typescript/adapters/node/index.d.ts +23 -0
- package/lib/typescript/adapters/node/index.d.ts.map +1 -0
- package/lib/typescript/adapters/node/storage.d.ts +23 -0
- package/lib/typescript/adapters/node/storage.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/DeviceManager.d.ts +64 -0
- package/lib/typescript/core/identity-session/DeviceManager.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/IdentityManager.d.ts +88 -0
- package/lib/typescript/core/identity-session/IdentityManager.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/IdentitySessionCore.d.ts +141 -0
- package/lib/typescript/core/identity-session/IdentitySessionCore.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/RefreshManager.d.ts +36 -0
- package/lib/typescript/core/identity-session/RefreshManager.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/SessionManager.d.ts +104 -0
- package/lib/typescript/core/identity-session/SessionManager.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/createIdentitySessionCore.d.ts +11 -0
- package/lib/typescript/core/identity-session/createIdentitySessionCore.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/errors.d.ts +63 -0
- package/lib/typescript/core/identity-session/errors.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/index.d.ts +14 -0
- package/lib/typescript/core/identity-session/index.d.ts.map +1 -0
- package/lib/typescript/core/identity-session/types.d.ts +196 -0
- package/lib/typescript/core/identity-session/types.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +1 -3
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +2 -2
- package/lib/typescript/index.d.ts +3 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +5 -36
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +3 -16
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +2 -25
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +7 -8
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts +1 -1
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +5 -5
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts +18 -0
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useIdentityTransfer.d.ts +24 -0
- package/lib/typescript/ui/hooks/useIdentityTransfer.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useTransferCodesPersistence.d.ts +6 -0
- package/lib/typescript/ui/hooks/useTransferCodesPersistence.d.ts.map +1 -0
- package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/utils/sessionHelpers.d.ts +1 -0
- package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +0 -2
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/expo/crypto.ts +55 -0
- package/src/adapters/expo/fetch.ts +28 -0
- package/src/adapters/expo/index.ts +51 -0
- package/src/adapters/expo/storage.ts +228 -0
- package/src/adapters/index.ts +40 -0
- package/src/adapters/node/crypto.ts +39 -0
- package/src/adapters/node/fetch.ts +59 -0
- package/src/adapters/node/index.ts +37 -0
- package/src/adapters/node/storage.ts +170 -0
- package/src/core/identity-session/DeviceManager.ts +273 -0
- package/src/core/identity-session/INTEGRATION_GUIDE.md +287 -0
- package/src/core/identity-session/IdentityManager.ts +474 -0
- package/src/core/identity-session/IdentitySessionCore.ts +464 -0
- package/src/core/identity-session/RefreshManager.ts +189 -0
- package/src/core/identity-session/SessionManager.ts +500 -0
- package/src/core/identity-session/createIdentitySessionCore.ts +19 -0
- package/src/core/identity-session/errors.ts +197 -0
- package/src/core/identity-session/index.ts +15 -0
- package/src/core/identity-session/types.ts +188 -0
- package/src/core/index.ts +3 -4
- package/src/index.ts +28 -3
- package/src/models/interfaces.ts +12 -39
- package/src/models/session.ts +6 -16
- package/src/ui/context/OxyContext.tsx +442 -871
- package/src/ui/hooks/auth/index.ts +1 -0
- package/src/ui/hooks/useAvatarPicker.ts +62 -0
- package/src/ui/hooks/useIdentityTransfer.ts +135 -0
- package/src/ui/hooks/useTransferCodesPersistence.ts +80 -0
- package/src/ui/screens/AccountCenterScreen.tsx +7 -2
- package/src/ui/screens/AccountSettingsScreen.tsx +15 -8
- package/src/ui/screens/AccountSwitcherScreen.tsx +2 -2
- package/src/ui/screens/ProfileScreen.tsx +10 -10
- package/src/ui/utils/sessionHelpers.ts +7 -0
- package/src/utils/index.ts +1 -2
- package/src/utils/sessionUtils.ts +8 -0
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +0 -732
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +0 -1
- package/lib/commonjs/ui/context/hooks/useDeviceManagement.js +0 -73
- package/lib/commonjs/ui/context/hooks/useDeviceManagement.js.map +0 -1
- package/lib/commonjs/ui/hooks/useDeviceManagement.js +0 -73
- package/lib/commonjs/ui/hooks/useDeviceManagement.js.map +0 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -281
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +0 -1
- package/lib/commonjs/utils/deviceManager.js +0 -177
- package/lib/commonjs/utils/deviceManager.js.map +0 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +0 -726
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +0 -1
- package/lib/module/ui/context/hooks/useDeviceManagement.js +0 -68
- package/lib/module/ui/context/hooks/useDeviceManagement.js.map +0 -1
- package/lib/module/ui/hooks/useDeviceManagement.js +0 -68
- package/lib/module/ui/hooks/useDeviceManagement.js.map +0 -1
- package/lib/module/ui/hooks/useSessionManagement.js +0 -276
- package/lib/module/ui/hooks/useSessionManagement.js.map +0 -1
- package/lib/module/utils/deviceManager.js +0 -171
- package/lib/module/utils/deviceManager.js.map +0 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +0 -59
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +0 -1
- package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts +0 -27
- package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useDeviceManagement.d.ts +0 -27
- package/lib/typescript/ui/hooks/useDeviceManagement.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +0 -41
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +0 -1
- package/lib/typescript/utils/deviceManager.d.ts +0 -66
- package/lib/typescript/utils/deviceManager.d.ts.map +0 -1
- package/src/ui/context/hooks/useAuthOperations.ts +0 -801
- package/src/ui/context/hooks/useDeviceManagement.ts +0 -108
- package/src/ui/hooks/useDeviceManagement.ts +0 -108
- package/src/ui/hooks/useSessionManagement.ts +0 -401
- package/src/utils/deviceManager.ts +0 -198
|
@@ -1,726 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
|
-
import { DeviceManager } from '../../../utils/deviceManager';
|
|
5
|
-
import { fetchSessionsWithFallback } from '../../utils/sessionHelpers';
|
|
6
|
-
import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
|
|
7
|
-
import { KeyManager, SignatureService } from '../../../crypto';
|
|
8
|
-
const LOGIN_ERROR_CODE = 'LOGIN_ERROR';
|
|
9
|
-
const REGISTER_ERROR_CODE = 'REGISTER_ERROR';
|
|
10
|
-
const LOGOUT_ERROR_CODE = 'LOGOUT_ERROR';
|
|
11
|
-
const LOGOUT_ALL_ERROR_CODE = 'LOGOUT_ALL_ERROR';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Authentication operations using public key cryptography.
|
|
15
|
-
* No passwords required - identity is based on ECDSA key pairs.
|
|
16
|
-
*/
|
|
17
|
-
export const useAuthOperations = ({
|
|
18
|
-
oxyServices,
|
|
19
|
-
storage,
|
|
20
|
-
sessions,
|
|
21
|
-
activeSessionId,
|
|
22
|
-
setActiveSessionId,
|
|
23
|
-
updateSessions,
|
|
24
|
-
saveActiveSessionId,
|
|
25
|
-
clearSessionState,
|
|
26
|
-
switchSession,
|
|
27
|
-
applyLanguagePreference,
|
|
28
|
-
onAuthStateChange,
|
|
29
|
-
onError,
|
|
30
|
-
loginSuccess,
|
|
31
|
-
loginFailure,
|
|
32
|
-
logoutStore,
|
|
33
|
-
setAuthState,
|
|
34
|
-
setIdentitySynced,
|
|
35
|
-
setSyncing,
|
|
36
|
-
logger
|
|
37
|
-
}) => {
|
|
38
|
-
/**
|
|
39
|
-
* Clear session data if identity has changed
|
|
40
|
-
* Internal helper to avoid code duplication
|
|
41
|
-
*/
|
|
42
|
-
const clearSessionsIfIdentityChanged = useCallback(async (oldPublicKey, newPublicKey) => {
|
|
43
|
-
if (oldPublicKey && oldPublicKey !== newPublicKey) {
|
|
44
|
-
if (__DEV__ && logger) {
|
|
45
|
-
logger('CRITICAL: Identity changed - clearing all session data', {
|
|
46
|
-
oldPublicKey: oldPublicKey.substring(0, 16) + '...',
|
|
47
|
-
newPublicKey: newPublicKey.substring(0, 16) + '...'
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Clear all session state to prevent old identity's data from showing up
|
|
52
|
-
await clearSessionState();
|
|
53
|
-
|
|
54
|
-
// Logout from auth store (clears user, isAuthenticated, etc.)
|
|
55
|
-
logoutStore();
|
|
56
|
-
|
|
57
|
-
// Force KeyManager cache invalidation
|
|
58
|
-
KeyManager.invalidateCache();
|
|
59
|
-
if (__DEV__ && logger) {
|
|
60
|
-
logger('Session state cleared for new identity');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}, [clearSessionState, logoutStore, logger]);
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Internal function to perform challenge-response sign in (works offline)
|
|
67
|
-
*/
|
|
68
|
-
const performSignIn = useCallback(async publicKey => {
|
|
69
|
-
const deviceFingerprintObj = DeviceManager.getDeviceFingerprint();
|
|
70
|
-
const deviceFingerprint = JSON.stringify(deviceFingerprintObj);
|
|
71
|
-
const deviceInfo = await DeviceManager.getDeviceInfo();
|
|
72
|
-
const deviceName = deviceInfo.deviceName || DeviceManager.getDefaultDeviceName();
|
|
73
|
-
let challenge;
|
|
74
|
-
let isOffline = false;
|
|
75
|
-
|
|
76
|
-
// Try to request challenge from server (online)
|
|
77
|
-
try {
|
|
78
|
-
const challengeResponse = await oxyServices.requestChallenge(publicKey);
|
|
79
|
-
challenge = challengeResponse.challenge;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
// Network error - generate challenge locally for offline sign-in
|
|
82
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
83
|
-
const isNetworkError = errorMessage.includes('Network') || errorMessage.includes('network') || errorMessage.includes('Failed to fetch') || errorMessage.includes('fetch failed') || error?.code === 'NETWORK_ERROR' || error?.status === 0;
|
|
84
|
-
if (isNetworkError) {
|
|
85
|
-
if (__DEV__ && logger) {
|
|
86
|
-
logger('Network unavailable, performing offline sign-in');
|
|
87
|
-
}
|
|
88
|
-
// Generate challenge locally
|
|
89
|
-
challenge = await SignatureService.generateChallenge();
|
|
90
|
-
isOffline = true;
|
|
91
|
-
} else {
|
|
92
|
-
// Re-throw non-network errors
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Note: Biometric authentication check should be handled by the app layer
|
|
98
|
-
// (e.g., accounts app) before calling signIn. The biometric preference is stored
|
|
99
|
-
// in local storage as 'oxy_biometric_enabled' and can be checked there.
|
|
100
|
-
|
|
101
|
-
// Sign the challenge
|
|
102
|
-
const {
|
|
103
|
-
challenge: signature,
|
|
104
|
-
timestamp
|
|
105
|
-
} = await SignatureService.signChallenge(challenge);
|
|
106
|
-
let fullUser;
|
|
107
|
-
let sessionResponse;
|
|
108
|
-
if (isOffline) {
|
|
109
|
-
// Offline sign-in: create local session and minimal user object
|
|
110
|
-
if (__DEV__ && logger) {
|
|
111
|
-
logger('Creating offline session');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Generate a local session ID
|
|
115
|
-
const localSessionId = `offline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
116
|
-
const localDeviceId = `device_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
117
|
-
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(); // 7 days
|
|
118
|
-
|
|
119
|
-
// Create minimal user object with publicKey as id
|
|
120
|
-
fullUser = {
|
|
121
|
-
id: publicKey,
|
|
122
|
-
// Use publicKey as id (per migration document)
|
|
123
|
-
publicKey,
|
|
124
|
-
username: '',
|
|
125
|
-
privacySettings: {}
|
|
126
|
-
};
|
|
127
|
-
sessionResponse = {
|
|
128
|
-
sessionId: localSessionId,
|
|
129
|
-
deviceId: localDeviceId,
|
|
130
|
-
expiresAt,
|
|
131
|
-
user: {
|
|
132
|
-
id: publicKey,
|
|
133
|
-
username: ''
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// Store offline session locally
|
|
138
|
-
const offlineSession = {
|
|
139
|
-
sessionId: localSessionId,
|
|
140
|
-
deviceId: localDeviceId,
|
|
141
|
-
expiresAt,
|
|
142
|
-
lastActive: new Date().toISOString(),
|
|
143
|
-
userId: publicKey,
|
|
144
|
-
isCurrent: true
|
|
145
|
-
};
|
|
146
|
-
setActiveSessionId(localSessionId);
|
|
147
|
-
await saveActiveSessionId(localSessionId);
|
|
148
|
-
updateSessions([offlineSession], {
|
|
149
|
-
merge: true
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Mark session as offline for later sync
|
|
153
|
-
if (storage) {
|
|
154
|
-
await storage.setItem(`oxy_session_${localSessionId}_offline`, 'true');
|
|
155
|
-
}
|
|
156
|
-
if (__DEV__ && logger) {
|
|
157
|
-
logger('Offline sign-in successful');
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
// Online sign-in: use normal flow
|
|
161
|
-
// Verify and create session
|
|
162
|
-
sessionResponse = await oxyServices.verifyChallenge(publicKey, challenge, signature, timestamp, deviceName, deviceFingerprint);
|
|
163
|
-
|
|
164
|
-
// Get token for the session
|
|
165
|
-
await oxyServices.getTokenBySession(sessionResponse.sessionId);
|
|
166
|
-
|
|
167
|
-
// Get full user data
|
|
168
|
-
fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
|
|
169
|
-
|
|
170
|
-
// IMPORTANT: user.id should be MongoDB ObjectId, not publicKey
|
|
171
|
-
// The API should return the correct id (ObjectId) from the database
|
|
172
|
-
// If it doesn't, we need to fix the API, not work around it here
|
|
173
|
-
// Validate that id is ObjectId format (24 hex characters)
|
|
174
|
-
if (fullUser.id && !/^[0-9a-fA-F]{24}$/.test(fullUser.id)) {
|
|
175
|
-
console.warn('[useAuthOperations] User.id is not MongoDB ObjectId format:', {
|
|
176
|
-
id: fullUser.id.substring(0, 20),
|
|
177
|
-
publicKey: fullUser.publicKey.substring(0, 20),
|
|
178
|
-
message: 'API should return MongoDB ObjectId as user.id, not publicKey'
|
|
179
|
-
});
|
|
180
|
-
// Don't override - let the API fix this issue
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Fetch device sessions
|
|
184
|
-
let allDeviceSessions = [];
|
|
185
|
-
try {
|
|
186
|
-
allDeviceSessions = await fetchSessionsWithFallback(oxyServices, sessionResponse.sessionId, {
|
|
187
|
-
fallbackDeviceId: sessionResponse.deviceId,
|
|
188
|
-
fallbackUserId: fullUser.id,
|
|
189
|
-
logger
|
|
190
|
-
});
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (__DEV__) {
|
|
193
|
-
console.warn('Failed to fetch device sessions after login:', error);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Check for existing session for same user
|
|
198
|
-
const existingSession = allDeviceSessions.find(session => session.userId?.toString() === fullUser.id?.toString() && session.sessionId !== sessionResponse.sessionId);
|
|
199
|
-
if (existingSession) {
|
|
200
|
-
// Logout duplicate session
|
|
201
|
-
try {
|
|
202
|
-
await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
|
|
203
|
-
} catch (logoutError) {
|
|
204
|
-
if (__DEV__) {
|
|
205
|
-
console.warn('Failed to logout duplicate session:', logoutError);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
await switchSession(existingSession.sessionId);
|
|
209
|
-
updateSessions(allDeviceSessions.filter(session => session.sessionId !== sessionResponse.sessionId), {
|
|
210
|
-
merge: false
|
|
211
|
-
});
|
|
212
|
-
onAuthStateChange?.(fullUser);
|
|
213
|
-
return fullUser;
|
|
214
|
-
}
|
|
215
|
-
setActiveSessionId(sessionResponse.sessionId);
|
|
216
|
-
await saveActiveSessionId(sessionResponse.sessionId);
|
|
217
|
-
updateSessions(allDeviceSessions, {
|
|
218
|
-
merge: true
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
await applyLanguagePreference(fullUser);
|
|
222
|
-
loginSuccess(fullUser);
|
|
223
|
-
onAuthStateChange?.(fullUser);
|
|
224
|
-
return fullUser;
|
|
225
|
-
}, [applyLanguagePreference, logger, loginSuccess, onAuthStateChange, oxyServices, saveActiveSessionId, setActiveSessionId, switchSession, updateSessions, storage]);
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Create a new identity (offline-first).
|
|
229
|
-
*
|
|
230
|
-
* Generates cryptographic keys locally without requiring server connection.
|
|
231
|
-
* Identity is based on the public/private key pair - no username or email required.
|
|
232
|
-
*
|
|
233
|
-
* IMPORTANT: This function only clears session data if the identity actually changes
|
|
234
|
-
* (i.e., a new key pair is generated). Retrying registration with the same identity
|
|
235
|
-
* will NOT clear session data.
|
|
236
|
-
*
|
|
237
|
-
* @param username - Optional username to set during registration (if online)
|
|
238
|
-
* @returns Object with synced status indicating if identity was registered with server
|
|
239
|
-
*/
|
|
240
|
-
const createIdentity = useCallback(async username => {
|
|
241
|
-
if (!storage) throw new Error('Storage not initialized');
|
|
242
|
-
setAuthState({
|
|
243
|
-
isLoading: true,
|
|
244
|
-
error: null
|
|
245
|
-
});
|
|
246
|
-
try {
|
|
247
|
-
// Get old public key before creating new identity
|
|
248
|
-
// This is used to detect if identity actually changed
|
|
249
|
-
const oldPublicKey = await KeyManager.getPublicKey().catch(() => null);
|
|
250
|
-
if (__DEV__ && logger) {
|
|
251
|
-
logger('Creating new identity', {
|
|
252
|
-
hadPreviousIdentity: !!oldPublicKey
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Generate new key pair directly (works offline)
|
|
257
|
-
const {
|
|
258
|
-
publicKey,
|
|
259
|
-
privateKey
|
|
260
|
-
} = await KeyManager.generateKeyPair();
|
|
261
|
-
await KeyManager.importKeyPair(privateKey);
|
|
262
|
-
if (__DEV__ && logger) {
|
|
263
|
-
logger('Identity keys generated', {
|
|
264
|
-
publicKey: publicKey.substring(0, 16) + '...'
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Only clear sessions if identity actually changed
|
|
269
|
-
// This prevents clearing sessions on registration retries
|
|
270
|
-
await clearSessionsIfIdentityChanged(oldPublicKey, publicKey);
|
|
271
|
-
|
|
272
|
-
// Mark as not synced initially
|
|
273
|
-
await storage.setItem('oxy_identity_synced', 'false');
|
|
274
|
-
setIdentitySynced(false);
|
|
275
|
-
|
|
276
|
-
// If username provided, try to register immediately (online only)
|
|
277
|
-
if (username) {
|
|
278
|
-
// Validate username format before attempting registration
|
|
279
|
-
const trimmedUsername = username.trim();
|
|
280
|
-
if (trimmedUsername && /^[a-zA-Z0-9]{3,30}$/.test(trimmedUsername)) {
|
|
281
|
-
try {
|
|
282
|
-
const {
|
|
283
|
-
signature,
|
|
284
|
-
timestamp
|
|
285
|
-
} = await SignatureService.createRegistrationSignature();
|
|
286
|
-
await oxyServices.register(publicKey, signature, timestamp, trimmedUsername);
|
|
287
|
-
|
|
288
|
-
// Mark as synced (Zustand store + storage)
|
|
289
|
-
await storage.setItem('oxy_identity_synced', 'true');
|
|
290
|
-
setIdentitySynced(true);
|
|
291
|
-
if (__DEV__ && logger) {
|
|
292
|
-
logger('Identity synced with server successfully with username');
|
|
293
|
-
}
|
|
294
|
-
return {
|
|
295
|
-
synced: true
|
|
296
|
-
};
|
|
297
|
-
} catch (syncError) {
|
|
298
|
-
// Offline or server error - identity created locally but not synced
|
|
299
|
-
if (__DEV__ && logger) {
|
|
300
|
-
logger('Identity created locally with username (offline), will sync when online', syncError);
|
|
301
|
-
}
|
|
302
|
-
return {
|
|
303
|
-
synced: false
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
} else {
|
|
307
|
-
// Invalid username format - log the issue
|
|
308
|
-
if (__DEV__ && logger) {
|
|
309
|
-
logger('Invalid username format, identity created without username', {
|
|
310
|
-
providedUsername: username.substring(0, 20)
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// No username provided or invalid format - defer registration until later
|
|
317
|
-
if (__DEV__ && logger) {
|
|
318
|
-
logger('Identity created locally without username, will register during sync');
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
synced: false
|
|
322
|
-
};
|
|
323
|
-
} catch (error) {
|
|
324
|
-
// CRITICAL: Never delete identity on error - it may have been successfully created
|
|
325
|
-
// Only log the error and let the user recover using their backup file
|
|
326
|
-
// Identity deletion should ONLY happen when explicitly requested by the user
|
|
327
|
-
if (__DEV__ && logger) {
|
|
328
|
-
logger('Error during identity creation (identity may still exist):', error);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Check if identity was actually created (keys exist)
|
|
332
|
-
const hasIdentity = await KeyManager.hasIdentity().catch(() => false);
|
|
333
|
-
if (hasIdentity) {
|
|
334
|
-
// Identity exists - don't delete it! Just mark as not synced
|
|
335
|
-
await storage.setItem('oxy_identity_synced', 'false').catch(() => {});
|
|
336
|
-
setIdentitySynced(false);
|
|
337
|
-
if (__DEV__ && logger) {
|
|
338
|
-
logger('Identity was created but sync failed - user can sync later');
|
|
339
|
-
}
|
|
340
|
-
} else {
|
|
341
|
-
// No identity exists - this was a generation failure, safe to clean up sync flag
|
|
342
|
-
await storage.removeItem('oxy_identity_synced').catch(() => {});
|
|
343
|
-
setIdentitySynced(false);
|
|
344
|
-
}
|
|
345
|
-
const message = handleAuthError(error, {
|
|
346
|
-
defaultMessage: 'Failed to create identity',
|
|
347
|
-
code: REGISTER_ERROR_CODE,
|
|
348
|
-
onError,
|
|
349
|
-
setAuthError: msg => setAuthState({
|
|
350
|
-
error: msg
|
|
351
|
-
}),
|
|
352
|
-
logger
|
|
353
|
-
});
|
|
354
|
-
loginFailure(message);
|
|
355
|
-
throw error;
|
|
356
|
-
} finally {
|
|
357
|
-
setAuthState({
|
|
358
|
-
isLoading: false
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
}, [oxyServices, storage, setAuthState, loginFailure, onError, logger, setIdentitySynced, clearSessionsIfIdentityChanged]);
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Check if identity is synced with server (reads from storage for persistence)
|
|
365
|
-
*/
|
|
366
|
-
const isIdentitySyncedFn = useCallback(async () => {
|
|
367
|
-
if (!storage) return true;
|
|
368
|
-
const synced = await storage.getItem('oxy_identity_synced');
|
|
369
|
-
const isSynced = synced !== 'false';
|
|
370
|
-
setIdentitySynced(isSynced);
|
|
371
|
-
return isSynced;
|
|
372
|
-
}, [storage, setIdentitySynced]);
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Sync local identity with server.
|
|
376
|
-
*
|
|
377
|
-
* Registers the identity with the server if not already registered.
|
|
378
|
-
* This function is idempotent - calling it multiple times is safe.
|
|
379
|
-
*
|
|
380
|
-
* TanStack Query handles offline mutations automatically, so this will
|
|
381
|
-
* retry automatically when connection is restored.
|
|
382
|
-
*
|
|
383
|
-
* @param username - Optional username to set during sync/registration
|
|
384
|
-
* @returns User object after successful sync and sign-in
|
|
385
|
-
*/
|
|
386
|
-
const syncIdentity = useCallback(async username => {
|
|
387
|
-
if (!storage) throw new Error('Storage not initialized');
|
|
388
|
-
setAuthState({
|
|
389
|
-
isLoading: true,
|
|
390
|
-
error: null
|
|
391
|
-
});
|
|
392
|
-
setSyncing(true);
|
|
393
|
-
try {
|
|
394
|
-
const publicKey = await KeyManager.getPublicKey();
|
|
395
|
-
if (!publicKey) {
|
|
396
|
-
throw new Error('No identity found on this device');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Check if already synced
|
|
400
|
-
const alreadySynced = await storage.getItem('oxy_identity_synced');
|
|
401
|
-
if (alreadySynced === 'true') {
|
|
402
|
-
setIdentitySynced(true);
|
|
403
|
-
// Identity is already synced, just sign in
|
|
404
|
-
return await performSignIn(publicKey);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Check if already registered on server
|
|
408
|
-
const {
|
|
409
|
-
registered
|
|
410
|
-
} = await oxyServices.checkPublicKeyRegistered(publicKey);
|
|
411
|
-
if (!registered) {
|
|
412
|
-
// Register with server (identity is just the publicKey)
|
|
413
|
-
const {
|
|
414
|
-
signature,
|
|
415
|
-
timestamp
|
|
416
|
-
} = await SignatureService.createRegistrationSignature();
|
|
417
|
-
await oxyServices.register(publicKey, signature, timestamp, username);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Mark as synced (Zustand store + storage)
|
|
421
|
-
await storage.setItem('oxy_identity_synced', 'true');
|
|
422
|
-
setIdentitySynced(true);
|
|
423
|
-
|
|
424
|
-
// Sign in
|
|
425
|
-
const user = await performSignIn(publicKey);
|
|
426
|
-
|
|
427
|
-
// TanStack Query will automatically retry any pending mutations
|
|
428
|
-
|
|
429
|
-
return user;
|
|
430
|
-
} catch (error) {
|
|
431
|
-
const message = handleAuthError(error, {
|
|
432
|
-
defaultMessage: 'Failed to sync identity',
|
|
433
|
-
code: REGISTER_ERROR_CODE,
|
|
434
|
-
onError,
|
|
435
|
-
setAuthError: msg => setAuthState({
|
|
436
|
-
error: msg
|
|
437
|
-
}),
|
|
438
|
-
logger
|
|
439
|
-
});
|
|
440
|
-
loginFailure(message);
|
|
441
|
-
throw error;
|
|
442
|
-
} finally {
|
|
443
|
-
setAuthState({
|
|
444
|
-
isLoading: false
|
|
445
|
-
});
|
|
446
|
-
setSyncing(false);
|
|
447
|
-
}
|
|
448
|
-
}, [oxyServices, storage, setAuthState, performSignIn, loginFailure, onError, logger, setSyncing, setIdentitySynced]);
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Import identity from encrypted backup file.
|
|
452
|
-
*
|
|
453
|
-
* Restores a previously created identity from an encrypted backup.
|
|
454
|
-
* The backup is decrypted using the provided password.
|
|
455
|
-
*
|
|
456
|
-
* IMPORTANT: This function clears session data only if importing a different
|
|
457
|
-
* identity than the one currently stored. Re-importing the same identity
|
|
458
|
-
* will NOT clear session data.
|
|
459
|
-
*
|
|
460
|
-
* @param backupData - The encrypted backup data object
|
|
461
|
-
* @param password - Password to decrypt the backup
|
|
462
|
-
* @param username - Optional username to set during registration if not yet registered
|
|
463
|
-
* @returns Object with synced status indicating if identity was registered with server
|
|
464
|
-
*/
|
|
465
|
-
const importIdentity = useCallback(async (backupData, password, username) => {
|
|
466
|
-
if (!storage) throw new Error('Storage not initialized');
|
|
467
|
-
|
|
468
|
-
// Validate arguments - ensure backupData is an object, not a string (old signature)
|
|
469
|
-
if (!backupData || typeof backupData !== 'object' || Array.isArray(backupData)) {
|
|
470
|
-
throw new Error('Invalid backup data. Please use the backup file import feature.');
|
|
471
|
-
}
|
|
472
|
-
if (!backupData.encrypted || !backupData.salt || !backupData.iv || !backupData.publicKey) {
|
|
473
|
-
throw new Error('Invalid backup data structure. Missing required fields.');
|
|
474
|
-
}
|
|
475
|
-
if (!password || typeof password !== 'string') {
|
|
476
|
-
throw new Error('Password is required for backup file import.');
|
|
477
|
-
}
|
|
478
|
-
setAuthState({
|
|
479
|
-
isLoading: true,
|
|
480
|
-
error: null
|
|
481
|
-
});
|
|
482
|
-
try {
|
|
483
|
-
// Get old public key before importing identity
|
|
484
|
-
// This is used to detect if identity actually changed
|
|
485
|
-
const oldPublicKey = await KeyManager.getPublicKey().catch(() => null);
|
|
486
|
-
if (__DEV__ && logger) {
|
|
487
|
-
logger('Importing identity from backup', {
|
|
488
|
-
hadPreviousIdentity: !!oldPublicKey,
|
|
489
|
-
backupPublicKey: backupData.publicKey.substring(0, 16) + '...'
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Decrypt private key from backup data
|
|
494
|
-
const Crypto = await import('expo-crypto');
|
|
495
|
-
|
|
496
|
-
// Convert hex strings to Uint8Array
|
|
497
|
-
const saltBytes = new Uint8Array(backupData.salt.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []);
|
|
498
|
-
const ivBytes = new Uint8Array(backupData.iv.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []);
|
|
499
|
-
|
|
500
|
-
// Derive key from password (same algorithm as EncryptedBackupGenerator)
|
|
501
|
-
const saltHex = Array.from(saltBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
502
|
-
let key = password + saltHex;
|
|
503
|
-
for (let i = 0; i < 10000; i++) {
|
|
504
|
-
key = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, key);
|
|
505
|
-
}
|
|
506
|
-
const keyBytes = new Uint8Array(32);
|
|
507
|
-
for (let i = 0; i < 64 && i < key.length; i += 2) {
|
|
508
|
-
keyBytes[i / 2] = parseInt(key.substring(i, i + 2), 16);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Decrypt private key (XOR decryption - same as encryption)
|
|
512
|
-
const encryptedBytes = Buffer.from(backupData.encrypted, 'base64');
|
|
513
|
-
const decryptedBytes = new Uint8Array(encryptedBytes.length);
|
|
514
|
-
for (let i = 0; i < encryptedBytes.length; i++) {
|
|
515
|
-
decryptedBytes[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length] ^ ivBytes[i % ivBytes.length];
|
|
516
|
-
}
|
|
517
|
-
const privateKey = new TextDecoder().decode(decryptedBytes);
|
|
518
|
-
|
|
519
|
-
// Import the key pair
|
|
520
|
-
const publicKey = await KeyManager.importKeyPair(privateKey);
|
|
521
|
-
if (__DEV__ && logger) {
|
|
522
|
-
logger('Identity keys imported', {
|
|
523
|
-
publicKey: publicKey.substring(0, 16) + '...'
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Verify public key matches
|
|
528
|
-
if (publicKey !== backupData.publicKey) {
|
|
529
|
-
throw new Error('Backup file is corrupted or password is incorrect');
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Only clear sessions if identity actually changed
|
|
533
|
-
// This prevents clearing sessions when re-importing the same identity
|
|
534
|
-
await clearSessionsIfIdentityChanged(oldPublicKey, publicKey);
|
|
535
|
-
|
|
536
|
-
// Mark as not synced initially (will check server status below)
|
|
537
|
-
await storage.setItem('oxy_identity_synced', 'false');
|
|
538
|
-
setIdentitySynced(false);
|
|
539
|
-
|
|
540
|
-
// Try to sync with server
|
|
541
|
-
try {
|
|
542
|
-
// Check if this identity is already registered
|
|
543
|
-
const {
|
|
544
|
-
registered
|
|
545
|
-
} = await oxyServices.checkPublicKeyRegistered(publicKey);
|
|
546
|
-
if (registered) {
|
|
547
|
-
// Identity exists on server, mark as synced
|
|
548
|
-
await storage.setItem('oxy_identity_synced', 'true');
|
|
549
|
-
setIdentitySynced(true);
|
|
550
|
-
return {
|
|
551
|
-
synced: true
|
|
552
|
-
};
|
|
553
|
-
} else {
|
|
554
|
-
// Need to register this identity
|
|
555
|
-
const {
|
|
556
|
-
signature,
|
|
557
|
-
timestamp
|
|
558
|
-
} = await SignatureService.createRegistrationSignature();
|
|
559
|
-
await oxyServices.register(publicKey, signature, timestamp, username);
|
|
560
|
-
await storage.setItem('oxy_identity_synced', 'true');
|
|
561
|
-
setIdentitySynced(true);
|
|
562
|
-
return {
|
|
563
|
-
synced: true
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
} catch (syncError) {
|
|
567
|
-
// Offline or server error - identity restored locally but not synced
|
|
568
|
-
if (__DEV__) {
|
|
569
|
-
console.log('[Auth] Identity imported locally, will sync when online:', syncError);
|
|
570
|
-
}
|
|
571
|
-
return {
|
|
572
|
-
synced: false
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
} catch (error) {
|
|
576
|
-
const message = handleAuthError(error, {
|
|
577
|
-
defaultMessage: 'Failed to import identity. Please check your password and backup file.',
|
|
578
|
-
code: REGISTER_ERROR_CODE,
|
|
579
|
-
onError,
|
|
580
|
-
setAuthError: msg => setAuthState({
|
|
581
|
-
error: msg
|
|
582
|
-
}),
|
|
583
|
-
logger
|
|
584
|
-
});
|
|
585
|
-
loginFailure(message);
|
|
586
|
-
throw error;
|
|
587
|
-
} finally {
|
|
588
|
-
setAuthState({
|
|
589
|
-
isLoading: false
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
}, [oxyServices, storage, setAuthState, loginFailure, onError, logger, setIdentitySynced, clearSessionsIfIdentityChanged]);
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* Sign in with existing identity on device
|
|
596
|
-
*/
|
|
597
|
-
const signIn = useCallback(async deviceName => {
|
|
598
|
-
if (!storage) throw new Error('Storage not initialized');
|
|
599
|
-
setAuthState({
|
|
600
|
-
isLoading: true,
|
|
601
|
-
error: null
|
|
602
|
-
});
|
|
603
|
-
try {
|
|
604
|
-
// Get stored public key
|
|
605
|
-
const publicKey = await KeyManager.getPublicKey();
|
|
606
|
-
if (!publicKey) {
|
|
607
|
-
throw new Error('No identity found on this device. Please create or import an identity.');
|
|
608
|
-
}
|
|
609
|
-
return await performSignIn(publicKey);
|
|
610
|
-
} catch (error) {
|
|
611
|
-
const message = handleAuthError(error, {
|
|
612
|
-
defaultMessage: 'Sign in failed',
|
|
613
|
-
code: LOGIN_ERROR_CODE,
|
|
614
|
-
onError,
|
|
615
|
-
setAuthError: msg => setAuthState({
|
|
616
|
-
error: msg
|
|
617
|
-
}),
|
|
618
|
-
logger
|
|
619
|
-
});
|
|
620
|
-
loginFailure(message);
|
|
621
|
-
throw error;
|
|
622
|
-
} finally {
|
|
623
|
-
setAuthState({
|
|
624
|
-
isLoading: false
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
}, [storage, setAuthState, performSignIn, loginFailure, onError, logger]);
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Logout from session
|
|
631
|
-
*/
|
|
632
|
-
const logout = useCallback(async targetSessionId => {
|
|
633
|
-
if (!activeSessionId) return;
|
|
634
|
-
try {
|
|
635
|
-
const sessionToLogout = targetSessionId || activeSessionId;
|
|
636
|
-
await oxyServices.logoutSession(activeSessionId, sessionToLogout);
|
|
637
|
-
const filteredSessions = sessions.filter(session => session.sessionId !== sessionToLogout);
|
|
638
|
-
updateSessions(filteredSessions, {
|
|
639
|
-
merge: false
|
|
640
|
-
});
|
|
641
|
-
if (sessionToLogout === activeSessionId) {
|
|
642
|
-
if (filteredSessions.length > 0) {
|
|
643
|
-
await switchSession(filteredSessions[0].sessionId);
|
|
644
|
-
} else {
|
|
645
|
-
await clearSessionState();
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
} catch (error) {
|
|
650
|
-
const isInvalid = isInvalidSessionError(error);
|
|
651
|
-
if (isInvalid && targetSessionId === activeSessionId) {
|
|
652
|
-
await clearSessionState();
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
handleAuthError(error, {
|
|
656
|
-
defaultMessage: 'Logout failed',
|
|
657
|
-
code: LOGOUT_ERROR_CODE,
|
|
658
|
-
onError,
|
|
659
|
-
setAuthError: msg => setAuthState({
|
|
660
|
-
error: msg
|
|
661
|
-
}),
|
|
662
|
-
logger,
|
|
663
|
-
status: isInvalid ? 401 : undefined
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
}, [activeSessionId, clearSessionState, logger, onError, oxyServices, sessions, setAuthState, switchSession, updateSessions]);
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Logout from all sessions
|
|
670
|
-
*/
|
|
671
|
-
const logoutAll = useCallback(async () => {
|
|
672
|
-
if (!activeSessionId) {
|
|
673
|
-
const error = new Error('No active session found');
|
|
674
|
-
setAuthState({
|
|
675
|
-
error: error.message
|
|
676
|
-
});
|
|
677
|
-
onError?.({
|
|
678
|
-
message: error.message,
|
|
679
|
-
code: LOGOUT_ALL_ERROR_CODE,
|
|
680
|
-
status: 404
|
|
681
|
-
});
|
|
682
|
-
throw error;
|
|
683
|
-
}
|
|
684
|
-
try {
|
|
685
|
-
await oxyServices.logoutAllSessions(activeSessionId);
|
|
686
|
-
await clearSessionState();
|
|
687
|
-
} catch (error) {
|
|
688
|
-
handleAuthError(error, {
|
|
689
|
-
defaultMessage: 'Logout all failed',
|
|
690
|
-
code: LOGOUT_ALL_ERROR_CODE,
|
|
691
|
-
onError,
|
|
692
|
-
setAuthError: msg => setAuthState({
|
|
693
|
-
error: msg
|
|
694
|
-
}),
|
|
695
|
-
logger
|
|
696
|
-
});
|
|
697
|
-
throw error instanceof Error ? error : new Error('Logout all failed');
|
|
698
|
-
}
|
|
699
|
-
}, [activeSessionId, clearSessionState, logger, onError, oxyServices, setAuthState]);
|
|
700
|
-
|
|
701
|
-
/**
|
|
702
|
-
* Check if device has an identity stored
|
|
703
|
-
*/
|
|
704
|
-
const hasIdentity = useCallback(async () => {
|
|
705
|
-
return KeyManager.hasIdentity();
|
|
706
|
-
}, []);
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Get the public key of the stored identity
|
|
710
|
-
*/
|
|
711
|
-
const getPublicKey = useCallback(async () => {
|
|
712
|
-
return KeyManager.getPublicKey();
|
|
713
|
-
}, []);
|
|
714
|
-
return {
|
|
715
|
-
createIdentity,
|
|
716
|
-
importIdentity,
|
|
717
|
-
signIn,
|
|
718
|
-
logout,
|
|
719
|
-
logoutAll,
|
|
720
|
-
hasIdentity,
|
|
721
|
-
getPublicKey,
|
|
722
|
-
isIdentitySynced: isIdentitySyncedFn,
|
|
723
|
-
syncIdentity
|
|
724
|
-
};
|
|
725
|
-
};
|
|
726
|
-
//# sourceMappingURL=useAuthOperations.js.map
|