@oxyhq/services 5.16.33 → 5.16.35
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/README.md +26 -8
- package/lib/commonjs/core/OxyServices.base.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/commonjs/core/services/AuthService.js +156 -0
- package/lib/commonjs/core/services/AuthService.js.map +1 -0
- package/lib/commonjs/core/services/SessionService.js +1 -2
- package/lib/commonjs/core/services/SessionService.js.map +1 -1
- package/lib/commonjs/core/services/SessionTransportService.js +64 -0
- package/lib/commonjs/core/services/SessionTransportService.js.map +1 -0
- package/lib/commonjs/core/services/TokenService.js +9 -17
- package/lib/commonjs/core/services/TokenService.js.map +1 -1
- package/lib/commonjs/core/services/UserService.js +123 -0
- package/lib/commonjs/core/services/UserService.js.map +1 -0
- package/lib/commonjs/core/services/index.js +34 -0
- package/lib/commonjs/core/services/index.js.map +1 -0
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +3 -2
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +28 -122
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +11 -11
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/shared/crypto/messageBuilders.js +79 -0
- package/lib/commonjs/shared/crypto/messageBuilders.js.map +1 -0
- package/lib/commonjs/shared/crypto/platform.js +118 -0
- package/lib/commonjs/shared/crypto/platform.js.map +1 -0
- package/lib/commonjs/shared/crypto/signature.js +191 -0
- package/lib/commonjs/shared/crypto/signature.js.map +1 -0
- package/lib/commonjs/shared/index.js +94 -0
- package/lib/commonjs/shared/index.js.map +1 -0
- package/lib/commonjs/shared/models/index.js +2 -0
- package/lib/commonjs/shared/models/index.js.map +1 -0
- package/lib/commonjs/shared/transport/index.js +260 -0
- package/lib/commonjs/shared/transport/index.js.map +1 -0
- package/lib/commonjs/shared/utils/index.js +82 -0
- package/lib/commonjs/shared/utils/index.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +4 -40
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +23 -61
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useLanguageManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +4 -12
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useLanguageManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -8
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +2 -0
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -11
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +11 -26
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/sessionUtils.js +1 -8
- package/lib/commonjs/utils/sessionUtils.js.map +1 -1
- package/lib/module/core/OxyServices.base.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/module/core/services/AuthService.js +151 -0
- package/lib/module/core/services/AuthService.js.map +1 -0
- package/lib/module/core/services/SessionService.js +1 -2
- package/lib/module/core/services/SessionService.js.map +1 -1
- package/lib/module/core/services/SessionTransportService.js +59 -0
- package/lib/module/core/services/SessionTransportService.js.map +1 -0
- package/lib/module/core/services/TokenService.js +9 -17
- package/lib/module/core/services/TokenService.js.map +1 -1
- package/lib/module/core/services/UserService.js +118 -0
- package/lib/module/core/services/UserService.js.map +1 -0
- package/lib/module/core/services/index.js +16 -0
- package/lib/module/core/services/index.js.map +1 -0
- package/lib/module/crypto/index.js +9 -0
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/crypto/keyManager.js +3 -2
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +26 -122
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +11 -11
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/shared/crypto/messageBuilders.js +70 -0
- package/lib/module/shared/crypto/messageBuilders.js.map +1 -0
- package/lib/module/shared/crypto/platform.js +112 -0
- package/lib/module/shared/crypto/platform.js.map +1 -0
- package/lib/module/shared/crypto/signature.js +186 -0
- package/lib/module/shared/crypto/signature.js.map +1 -0
- package/lib/module/shared/index.js +30 -0
- package/lib/module/shared/index.js.map +1 -0
- package/lib/module/shared/models/index.js +2 -0
- package/lib/module/shared/models/index.js.map +1 -0
- package/lib/module/shared/transport/index.js +254 -0
- package/lib/module/shared/transport/index.js.map +1 -0
- package/lib/module/shared/utils/index.js +74 -0
- package/lib/module/shared/utils/index.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +4 -40
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +23 -61
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/hooks/useLanguageManagement.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js +5 -13
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/useLanguageManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +0 -8
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -0
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +2 -11
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +11 -26
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/sessionUtils.js +1 -8
- package/lib/module/utils/sessionUtils.js.map +1 -1
- package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.security.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +2 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +13 -13
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/core/services/AuthService.d.ts +50 -0
- package/lib/typescript/core/services/AuthService.d.ts.map +1 -0
- package/lib/typescript/core/services/SessionService.d.ts +3 -5
- package/lib/typescript/core/services/SessionService.d.ts.map +1 -1
- package/lib/typescript/core/services/SessionTransportService.d.ts +31 -0
- package/lib/typescript/core/services/SessionTransportService.d.ts.map +1 -0
- package/lib/typescript/core/services/TokenService.d.ts +3 -8
- package/lib/typescript/core/services/TokenService.d.ts.map +1 -1
- package/lib/typescript/core/services/UserService.d.ts +39 -0
- package/lib/typescript/core/services/UserService.d.ts.map +1 -0
- package/lib/typescript/core/services/index.d.ts +13 -0
- package/lib/typescript/core/services/index.d.ts.map +1 -0
- package/lib/typescript/crypto/index.d.ts +9 -0
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +10 -13
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +15 -69
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +2 -4
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/shared/crypto/messageBuilders.d.ts +38 -0
- package/lib/typescript/shared/crypto/messageBuilders.d.ts.map +1 -0
- package/lib/typescript/shared/crypto/platform.d.ts +54 -0
- package/lib/typescript/shared/crypto/platform.d.ts.map +1 -0
- package/lib/typescript/shared/crypto/signature.d.ts +72 -0
- package/lib/typescript/shared/crypto/signature.d.ts.map +1 -0
- package/lib/typescript/shared/index.d.ts +20 -0
- package/lib/typescript/shared/index.d.ts.map +1 -0
- package/lib/typescript/shared/models/index.d.ts +163 -0
- package/lib/typescript/shared/models/index.d.ts.map +1 -0
- package/lib/typescript/shared/transport/index.d.ts +73 -0
- package/lib/typescript/shared/transport/index.d.ts.map +1 -0
- package/lib/typescript/shared/utils/index.d.ts +28 -0
- package/lib/typescript/shared/utils/index.d.ts.map +1 -0
- package/lib/typescript/ui/context/OxyContext.d.ts +2 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +2 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +2 -1
- package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts +2 -1
- package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +2 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/index.d.ts +1 -1
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/ui/utils/sessionHelpers.d.ts +2 -6
- package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/OxyServices.base.ts +2 -1
- package/src/core/mixins/OxyServices.auth.ts +1 -1
- package/src/core/mixins/OxyServices.user.ts +2 -1
- package/src/core/mixins/OxyServices.utility.ts +2 -1
- package/src/core/services/AuthService.ts +153 -0
- package/src/core/services/SessionService.ts +3 -5
- package/src/core/services/SessionTransportService.ts +69 -0
- package/src/core/services/TokenService.ts +10 -18
- package/src/core/services/UserService.ts +125 -0
- package/src/core/services/index.ts +14 -0
- package/src/crypto/index.ts +9 -0
- package/src/crypto/keyManager.ts +3 -2
- package/src/crypto/signatureService.ts +43 -142
- package/src/index.ts +3 -2
- package/src/models/interfaces.ts +21 -74
- package/src/models/session.ts +3 -5
- package/src/shared/crypto/messageBuilders.ts +89 -0
- package/src/shared/crypto/platform.ts +140 -0
- package/src/shared/crypto/signature.ts +235 -0
- package/src/shared/index.ts +28 -0
- package/src/shared/models/index.ts +173 -0
- package/src/shared/transport/index.ts +349 -0
- package/src/shared/utils/index.ts +73 -0
- package/src/ui/context/OxyContext.tsx +22 -57
- package/src/ui/context/hooks/useAuthOperations.ts +33 -65
- package/src/ui/context/hooks/useLanguageManagement.ts +2 -1
- package/src/ui/hooks/auth/index.ts +0 -2
- package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
- package/src/ui/hooks/mutations/useServicesMutations.ts +1 -1
- package/src/ui/hooks/queries/useAccountQueries.ts +1 -1
- package/src/ui/hooks/queries/useServicesQueries.ts +3 -8
- package/src/ui/hooks/useLanguageManagement.ts +2 -1
- package/src/ui/hooks/useSessionManagement.ts +3 -9
- package/src/ui/index.ts +2 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +6 -6
- package/src/ui/screens/AccountSwitcherScreen.tsx +1 -1
- package/src/ui/screens/OxyAuthScreen.tsx +2 -11
- package/src/ui/screens/ProfileScreen.tsx +1 -1
- package/src/ui/stores/authStore.ts +1 -1
- package/src/ui/types/navigation.ts +1 -1
- package/src/ui/utils/avatarUtils.ts +1 -1
- package/src/ui/utils/sessionHelpers.ts +15 -32
- package/src/utils/sessionUtils.ts +1 -8
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport Abstraction
|
|
3
|
+
*
|
|
4
|
+
* Unified transport layer for WebSocket, SSE, and polling.
|
|
5
|
+
* Provides automatic fallback and unified configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type TransportType = 'websocket' | 'sse' | 'polling';
|
|
9
|
+
|
|
10
|
+
export interface TransportConfig {
|
|
11
|
+
baseURL: string;
|
|
12
|
+
namespace?: string;
|
|
13
|
+
sessionToken?: string;
|
|
14
|
+
accessToken?: string;
|
|
15
|
+
pollingInterval?: number; // milliseconds
|
|
16
|
+
reconnectAttempts?: number;
|
|
17
|
+
reconnectDelay?: number; // milliseconds
|
|
18
|
+
timeout?: number; // milliseconds
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TransportUpdate {
|
|
22
|
+
status: 'authorized' | 'cancelled' | 'expired' | 'pending';
|
|
23
|
+
sessionId?: string;
|
|
24
|
+
publicKey?: string;
|
|
25
|
+
userId?: string;
|
|
26
|
+
username?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TransportCallbacks {
|
|
31
|
+
onUpdate?: (update: TransportUpdate) => void;
|
|
32
|
+
onConnect?: () => void;
|
|
33
|
+
onDisconnect?: () => void;
|
|
34
|
+
onError?: (error: Error) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Transport Interface
|
|
39
|
+
* All transport implementations must implement this
|
|
40
|
+
*/
|
|
41
|
+
export interface Transport {
|
|
42
|
+
/**
|
|
43
|
+
* Connect to the transport
|
|
44
|
+
*/
|
|
45
|
+
connect(): Promise<void>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Disconnect from the transport
|
|
49
|
+
*/
|
|
50
|
+
disconnect(): void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if currently connected
|
|
54
|
+
*/
|
|
55
|
+
isConnected(): boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the transport type
|
|
59
|
+
*/
|
|
60
|
+
getType(): TransportType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Transport Factory
|
|
65
|
+
* Creates the appropriate transport based on availability and configuration
|
|
66
|
+
*/
|
|
67
|
+
export class TransportFactory {
|
|
68
|
+
/**
|
|
69
|
+
* Create a transport with automatic fallback
|
|
70
|
+
* Tries WebSocket first, then SSE, then polling
|
|
71
|
+
*/
|
|
72
|
+
static async create(
|
|
73
|
+
config: TransportConfig,
|
|
74
|
+
callbacks: TransportCallbacks
|
|
75
|
+
): Promise<Transport> {
|
|
76
|
+
// Try WebSocket first (best for real-time)
|
|
77
|
+
if (await this.isWebSocketAvailable()) {
|
|
78
|
+
try {
|
|
79
|
+
return new WebSocketTransport(config, callbacks);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Fall through to SSE
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Try SSE second (good for one-way updates)
|
|
86
|
+
if (await this.isSSEAvailable()) {
|
|
87
|
+
try {
|
|
88
|
+
return new SSETransport(config, callbacks);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Fall through to polling
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Fall back to polling (always available)
|
|
95
|
+
return new PollingTransport(config, callbacks);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if WebSocket is available
|
|
100
|
+
*/
|
|
101
|
+
private static async isWebSocketAvailable(): Promise<boolean> {
|
|
102
|
+
// WebSocket is available in browsers and Node.js with socket.io-client
|
|
103
|
+
return typeof window !== 'undefined' || typeof process !== 'undefined';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if SSE is available
|
|
108
|
+
*/
|
|
109
|
+
private static async isSSEAvailable(): Promise<boolean> {
|
|
110
|
+
// SSE is available in browsers via EventSource
|
|
111
|
+
// In Node.js, would need a polyfill or library
|
|
112
|
+
return typeof EventSource !== 'undefined' ||
|
|
113
|
+
(typeof window !== 'undefined' && 'EventSource' in window);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* WebSocket Transport Implementation
|
|
119
|
+
* Uses socket.io-client for WebSocket connections
|
|
120
|
+
*/
|
|
121
|
+
class WebSocketTransport implements Transport {
|
|
122
|
+
private socket: any = null;
|
|
123
|
+
private config: TransportConfig;
|
|
124
|
+
private callbacks: TransportCallbacks;
|
|
125
|
+
private connected = false;
|
|
126
|
+
|
|
127
|
+
constructor(config: TransportConfig, callbacks: TransportCallbacks) {
|
|
128
|
+
this.config = config;
|
|
129
|
+
this.callbacks = callbacks;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async connect(): Promise<void> {
|
|
133
|
+
// Dynamic import to avoid bundling socket.io-client if not needed
|
|
134
|
+
let io: any;
|
|
135
|
+
try {
|
|
136
|
+
io = (await import('socket.io-client')).default;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw new Error('socket.io-client is required for WebSocket transport. Install it as a dependency.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const url = this.config.namespace
|
|
142
|
+
? `${this.config.baseURL}/${this.config.namespace}`
|
|
143
|
+
: this.config.baseURL;
|
|
144
|
+
|
|
145
|
+
const socketOptions: any = {
|
|
146
|
+
transports: ['websocket', 'polling'],
|
|
147
|
+
reconnection: true,
|
|
148
|
+
reconnectionAttempts: this.config.reconnectAttempts ?? 3,
|
|
149
|
+
reconnectionDelay: this.config.reconnectDelay ?? 1000,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (this.config.accessToken) {
|
|
153
|
+
socketOptions.auth = { token: this.config.accessToken };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.socket = io(url, socketOptions);
|
|
157
|
+
|
|
158
|
+
this.socket.on('connect', () => {
|
|
159
|
+
this.connected = true;
|
|
160
|
+
if (this.config.sessionToken) {
|
|
161
|
+
this.socket.emit('join', this.config.sessionToken);
|
|
162
|
+
}
|
|
163
|
+
this.callbacks.onConnect?.();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.socket.on('auth_update', (payload: TransportUpdate) => {
|
|
167
|
+
this.callbacks.onUpdate?.(payload);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.socket.on('connect_error', (error: Error) => {
|
|
171
|
+
this.callbacks.onError?.(error);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
this.socket.on('disconnect', () => {
|
|
175
|
+
this.connected = false;
|
|
176
|
+
this.callbacks.onDisconnect?.();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
disconnect(): void {
|
|
181
|
+
if (this.socket) {
|
|
182
|
+
this.socket.disconnect();
|
|
183
|
+
this.socket = null;
|
|
184
|
+
this.connected = false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
isConnected(): boolean {
|
|
189
|
+
return this.connected && this.socket?.connected === true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getType(): TransportType {
|
|
193
|
+
return 'websocket';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* SSE Transport Implementation
|
|
199
|
+
* Uses EventSource for Server-Sent Events
|
|
200
|
+
*/
|
|
201
|
+
class SSETransport implements Transport {
|
|
202
|
+
private eventSource: EventSource | null = null;
|
|
203
|
+
private config: TransportConfig;
|
|
204
|
+
private callbacks: TransportCallbacks;
|
|
205
|
+
private connected = false;
|
|
206
|
+
|
|
207
|
+
constructor(config: TransportConfig, callbacks: TransportCallbacks) {
|
|
208
|
+
this.config = config;
|
|
209
|
+
this.callbacks = callbacks;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async connect(): Promise<void> {
|
|
213
|
+
if (typeof EventSource === 'undefined') {
|
|
214
|
+
throw new Error('EventSource is not available in this environment');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const url = this.config.sessionToken
|
|
218
|
+
? `${this.config.baseURL}/auth/session/stream/${this.config.sessionToken}`
|
|
219
|
+
: `${this.config.baseURL}/auth/session/stream`;
|
|
220
|
+
|
|
221
|
+
this.eventSource = new EventSource(url);
|
|
222
|
+
|
|
223
|
+
this.eventSource.onopen = () => {
|
|
224
|
+
this.connected = true;
|
|
225
|
+
this.callbacks.onConnect?.();
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
this.eventSource.onmessage = (event) => {
|
|
229
|
+
try {
|
|
230
|
+
const update = JSON.parse(event.data) as TransportUpdate;
|
|
231
|
+
this.callbacks.onUpdate?.(update);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.callbacks.onError?.(error as Error);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
this.eventSource.onerror = (error) => {
|
|
238
|
+
this.callbacks.onError?.(new Error('SSE connection error'));
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
disconnect(): void {
|
|
243
|
+
if (this.eventSource) {
|
|
244
|
+
this.eventSource.close();
|
|
245
|
+
this.eventSource = null;
|
|
246
|
+
this.connected = false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
isConnected(): boolean {
|
|
251
|
+
return this.connected && this.eventSource?.readyState === EventSource.OPEN;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
getType(): TransportType {
|
|
255
|
+
return 'sse';
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Polling Transport Implementation
|
|
261
|
+
* Uses HTTP polling as fallback
|
|
262
|
+
*/
|
|
263
|
+
class PollingTransport implements Transport {
|
|
264
|
+
private intervalId: ReturnType<typeof setInterval> | null = null;
|
|
265
|
+
private config: TransportConfig;
|
|
266
|
+
private callbacks: TransportCallbacks;
|
|
267
|
+
private connected = false;
|
|
268
|
+
private abortController: AbortController | null = null;
|
|
269
|
+
|
|
270
|
+
constructor(config: TransportConfig, callbacks: TransportCallbacks) {
|
|
271
|
+
this.config = config;
|
|
272
|
+
this.callbacks = callbacks;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async connect(): Promise<void> {
|
|
276
|
+
this.connected = true;
|
|
277
|
+
this.callbacks.onConnect?.();
|
|
278
|
+
|
|
279
|
+
const poll = async () => {
|
|
280
|
+
if (!this.config.sessionToken) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
this.abortController = new AbortController();
|
|
286
|
+
const response = await fetch(
|
|
287
|
+
`${this.config.baseURL}/api/auth/session/status/${this.config.sessionToken}`,
|
|
288
|
+
{
|
|
289
|
+
signal: this.abortController.signal,
|
|
290
|
+
headers: this.config.accessToken
|
|
291
|
+
? { Authorization: `Bearer ${this.config.accessToken}` }
|
|
292
|
+
: {},
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (!response.ok) {
|
|
297
|
+
throw new Error(`Polling failed: ${response.statusText}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const data = await response.json();
|
|
301
|
+
|
|
302
|
+
if (data.authorized && data.sessionId) {
|
|
303
|
+
this.callbacks.onUpdate?.({
|
|
304
|
+
status: 'authorized',
|
|
305
|
+
sessionId: data.sessionId,
|
|
306
|
+
publicKey: data.publicKey,
|
|
307
|
+
});
|
|
308
|
+
} else if (data.status === 'expired') {
|
|
309
|
+
this.callbacks.onUpdate?.({ status: 'expired' });
|
|
310
|
+
} else if (data.status === 'cancelled') {
|
|
311
|
+
this.callbacks.onUpdate?.({ status: 'cancelled' });
|
|
312
|
+
}
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error instanceof Error && error.name !== 'AbortError') {
|
|
315
|
+
this.callbacks.onError?.(error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Poll immediately, then at intervals
|
|
321
|
+
await poll();
|
|
322
|
+
this.intervalId = setInterval(
|
|
323
|
+
poll,
|
|
324
|
+
this.config.pollingInterval ?? 3000
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
disconnect(): void {
|
|
329
|
+
if (this.intervalId) {
|
|
330
|
+
clearInterval(this.intervalId);
|
|
331
|
+
this.intervalId = null;
|
|
332
|
+
}
|
|
333
|
+
if (this.abortController) {
|
|
334
|
+
this.abortController.abort();
|
|
335
|
+
this.abortController = null;
|
|
336
|
+
}
|
|
337
|
+
this.connected = false;
|
|
338
|
+
this.callbacks.onDisconnect?.();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
isConnected(): boolean {
|
|
342
|
+
return this.connected && this.intervalId !== null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
getType(): TransportType {
|
|
346
|
+
return 'polling';
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common utility functions used across Accounts, Services SDK, and API packages.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get a shortened display version of a public key
|
|
9
|
+
* Format: first 8 chars ... last 8 chars
|
|
10
|
+
*/
|
|
11
|
+
export function shortenPublicKey(publicKey: string): string {
|
|
12
|
+
if (publicKey.length <= 16) return publicKey;
|
|
13
|
+
return `${publicKey.slice(0, 8)}...${publicKey.slice(-8)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate a secure random session token
|
|
18
|
+
* Uses crypto.randomBytes (Node) or Web Crypto API (Browser/RN)
|
|
19
|
+
*/
|
|
20
|
+
export async function generateSessionToken(size: number = 32): Promise<string> {
|
|
21
|
+
// Use platform-appropriate random bytes
|
|
22
|
+
if (typeof window !== 'undefined' && window.crypto) {
|
|
23
|
+
// Web platform
|
|
24
|
+
const array = new Uint8Array(size);
|
|
25
|
+
window.crypto.getRandomValues(array);
|
|
26
|
+
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
30
|
+
// Node.js platform
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
32
|
+
const getCrypto = new Function('return require("crypto")');
|
|
33
|
+
const crypto = getCrypto();
|
|
34
|
+
return crypto.randomBytes(size).toString('hex');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// React Native - will be handled by platform adapter
|
|
38
|
+
// For now, throw an error if we can't determine platform
|
|
39
|
+
throw new Error('Unable to generate session token: no crypto implementation available');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a session token synchronously (Node.js only)
|
|
44
|
+
*/
|
|
45
|
+
export function generateSessionTokenSync(size: number = 32): string {
|
|
46
|
+
if (typeof process === 'undefined' || !process.versions?.node) {
|
|
47
|
+
throw new Error('generateSessionTokenSync can only be used in Node.js');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
51
|
+
const getCrypto = new Function('return require("crypto")');
|
|
52
|
+
const crypto = getCrypto();
|
|
53
|
+
return crypto.randomBytes(size).toString('hex');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert bytes to hex string
|
|
58
|
+
*/
|
|
59
|
+
export function bytesToHex(bytes: Uint8Array): string {
|
|
60
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert hex string to bytes
|
|
65
|
+
*/
|
|
66
|
+
export function hexToBytes(hex: string): Uint8Array {
|
|
67
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
68
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
69
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
70
|
+
}
|
|
71
|
+
return bytes;
|
|
72
|
+
}
|
|
73
|
+
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
} from 'react';
|
|
12
12
|
import { Platform } from 'react-native';
|
|
13
13
|
import { OxyServices } from '../../core';
|
|
14
|
-
import type {
|
|
14
|
+
import type { ApiError } from '../../models/interfaces';
|
|
15
|
+
import type { User } from '../../shared';
|
|
15
16
|
import type { ClientSession } from '../../models/session';
|
|
16
17
|
import { toast } from '../../lib/sonner';
|
|
17
18
|
import { useAuthStore, type AuthState } from '../stores/authStore';
|
|
@@ -222,7 +223,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
222
223
|
// CRITICAL: Invalidate cache on app startup to ensure fresh state check
|
|
223
224
|
// This prevents stale cache from previous session from showing incorrect state
|
|
224
225
|
KeyManager.invalidateCache();
|
|
225
|
-
|
|
226
|
+
|
|
226
227
|
// Check if identity exists and verify integrity
|
|
227
228
|
const hasIdentity = await KeyManager.hasIdentity();
|
|
228
229
|
if (hasIdentity) {
|
|
@@ -421,7 +422,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
421
422
|
if (pendingTransfers.length > 0) {
|
|
422
423
|
const activeTransferId = getActiveTransferId();
|
|
423
424
|
const hasActiveTransfer = activeTransferId && pendingTransfers.some((t: { transferId: string; data: any }) => t.transferId === activeTransferId);
|
|
424
|
-
|
|
425
|
+
|
|
425
426
|
if (hasActiveTransfer) {
|
|
426
427
|
throw new Error(
|
|
427
428
|
'Cannot delete identity: An active identity transfer is in progress. ' +
|
|
@@ -485,11 +486,11 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
485
486
|
if (wasOffline) {
|
|
486
487
|
const now = Date.now();
|
|
487
488
|
const timeSinceLastLog = now - lastReconnectionLog;
|
|
488
|
-
|
|
489
|
+
|
|
489
490
|
if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
|
|
490
491
|
logger('Network reconnected, checking identity sync...');
|
|
491
492
|
lastReconnectionLog = now;
|
|
492
|
-
|
|
493
|
+
|
|
493
494
|
// Sync identity first (if not synced)
|
|
494
495
|
try {
|
|
495
496
|
const hasIdentityValue = await hasIdentity();
|
|
@@ -520,7 +521,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
520
521
|
// This is handled by useCheckPendingTransfers hook which runs automatically
|
|
521
522
|
// when authenticated and online
|
|
522
523
|
}
|
|
523
|
-
|
|
524
|
+
|
|
524
525
|
// TanStack Query will automatically retry pending mutations
|
|
525
526
|
// Reset flag immediately after processing (whether logged or not)
|
|
526
527
|
wasOffline = false;
|
|
@@ -580,22 +581,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
580
581
|
setTokenReady(false);
|
|
581
582
|
|
|
582
583
|
try {
|
|
583
|
-
// CRITICAL: Get current local identity (publicKey) before restoring sessions
|
|
584
|
-
// Sessions must belong to the current local identity stored in Accounts app
|
|
585
|
-
let currentPublicKey: string | null = null;
|
|
586
|
-
try {
|
|
587
|
-
if (Platform.OS !== 'web') {
|
|
588
|
-
// Only check identity on native platforms (web doesn't have local identity)
|
|
589
|
-
// KeyManager is already imported at the top of the file
|
|
590
|
-
currentPublicKey = await KeyManager.getPublicKey();
|
|
591
|
-
}
|
|
592
|
-
} catch (identityError) {
|
|
593
|
-
if (__DEV__) {
|
|
594
|
-
logger('Failed to get current local identity during session restoration', identityError);
|
|
595
|
-
}
|
|
596
|
-
// Continue without identity check if it fails (e.g., on web platform)
|
|
597
|
-
}
|
|
598
|
-
|
|
599
584
|
const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
|
|
600
585
|
const storedSessionIds: string[] = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
|
|
601
586
|
const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
|
|
@@ -608,25 +593,12 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
608
593
|
const validation = await oxyServices.validateSession(sessionId, { useHeaderValidation: true });
|
|
609
594
|
if (validation?.valid && validation.user) {
|
|
610
595
|
const now = new Date();
|
|
611
|
-
// user.id is now publicKey (canonical identity)
|
|
612
|
-
const sessionPublicKey = validation.user.publicKey || validation.user.id || '';
|
|
613
|
-
|
|
614
|
-
// IDENTITY BINDING: Only restore sessions that match current local identity
|
|
615
|
-
// If we have a current local identity, filter out sessions that don't match
|
|
616
|
-
if (currentPublicKey && sessionPublicKey !== currentPublicKey) {
|
|
617
|
-
if (__DEV__) {
|
|
618
|
-
logger(`Skipping session ${sessionId.substring(0, 8)}... - belongs to different identity (${sessionPublicKey.substring(0, 16)}... vs ${currentPublicKey.substring(0, 16)}...)`);
|
|
619
|
-
}
|
|
620
|
-
continue; // Skip this session - it belongs to a different identity
|
|
621
|
-
}
|
|
622
|
-
|
|
623
596
|
validSessions.push({
|
|
624
597
|
sessionId,
|
|
625
598
|
deviceId: '',
|
|
626
599
|
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
627
600
|
lastActive: now.toISOString(),
|
|
628
|
-
|
|
629
|
-
userId: validation.user.id?.toString(), // Optional MongoDB ObjectId for backward compatibility
|
|
601
|
+
userId: validation.user.id?.toString() ?? '',
|
|
630
602
|
isCurrent: sessionId === storedActiveSessionId,
|
|
631
603
|
});
|
|
632
604
|
}
|
|
@@ -644,13 +616,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
644
616
|
|
|
645
617
|
if (validSessions.length > 0) {
|
|
646
618
|
updateSessions(validSessions, { merge: false });
|
|
647
|
-
} else if (currentPublicKey && storedSessionIds.length > 0) {
|
|
648
|
-
// All sessions were filtered out due to identity mismatch - clear stored sessions
|
|
649
|
-
if (__DEV__) {
|
|
650
|
-
logger('All stored sessions belong to different identity - clearing session storage');
|
|
651
|
-
}
|
|
652
|
-
await storage.removeItem(storageKeys.sessionIds);
|
|
653
|
-
await storage.removeItem(storageKeys.activeSessionId);
|
|
654
619
|
}
|
|
655
620
|
}
|
|
656
621
|
|
|
@@ -762,9 +727,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
762
727
|
return isAuthenticatedFromStore || isAuthenticatedFromSessions;
|
|
763
728
|
}, [isAuthenticatedFromStore, isAuthenticatedFromSessions]);
|
|
764
729
|
|
|
765
|
-
// Get userId from JWT token for socket room matching
|
|
766
|
-
//
|
|
767
|
-
//
|
|
730
|
+
// Get userId from JWT token (MongoDB ObjectId) for socket room matching
|
|
731
|
+
// user.id is set to publicKey for compatibility, but socket rooms use MongoDB ObjectId
|
|
732
|
+
// The JWT token's userId field contains the MongoDB ObjectId
|
|
768
733
|
const userId = oxyServices.getCurrentUserId() || user?.id;
|
|
769
734
|
|
|
770
735
|
// Use Zustand store for transfer state management
|
|
@@ -784,16 +749,16 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
784
749
|
// Load transfer codes
|
|
785
750
|
const storedCodes = await storage.getItem(TRANSFER_CODES_STORAGE_KEY);
|
|
786
751
|
const storedActiveTransferId = await storage.getItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
787
|
-
|
|
752
|
+
|
|
788
753
|
const parsedCodes = storedCodes ? JSON.parse(storedCodes) : {};
|
|
789
754
|
const activeTransferId = storedActiveTransferId || null;
|
|
790
|
-
|
|
755
|
+
|
|
791
756
|
// Restore to Zustand store (store handles validation and expiration)
|
|
792
757
|
restoreFromStorage(parsedCodes, activeTransferId);
|
|
793
758
|
markRestored();
|
|
794
|
-
|
|
759
|
+
|
|
795
760
|
if (__DEV__ && Object.keys(parsedCodes).length > 0) {
|
|
796
|
-
logger('Restored transfer codes from storage', {
|
|
761
|
+
logger('Restored transfer codes from storage', {
|
|
797
762
|
count: Object.keys(parsedCodes).length,
|
|
798
763
|
hasActiveTransfer: !!activeTransferId,
|
|
799
764
|
});
|
|
@@ -818,7 +783,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
818
783
|
const persistTransferCodes = async () => {
|
|
819
784
|
try {
|
|
820
785
|
await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(transferCodes));
|
|
821
|
-
|
|
786
|
+
|
|
822
787
|
if (activeTransferId) {
|
|
823
788
|
await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, activeTransferId);
|
|
824
789
|
} else {
|
|
@@ -846,7 +811,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
846
811
|
// Transfer code management functions using Zustand store
|
|
847
812
|
const storeTransferCode = useCallback(async (transferId: string, code: string, sourceDeviceId: string | null, publicKey: string) => {
|
|
848
813
|
storeTransferCodeStore(transferId, code, sourceDeviceId, publicKey);
|
|
849
|
-
|
|
814
|
+
|
|
850
815
|
if (__DEV__) {
|
|
851
816
|
logger('Stored transfer code', { transferId, sourceDeviceId, publicKey: publicKey.substring(0, 16) + '...' });
|
|
852
817
|
}
|
|
@@ -858,7 +823,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
858
823
|
|
|
859
824
|
const updateTransferState = useCallback(async (transferId: string, state: 'pending' | 'completed' | 'failed') => {
|
|
860
825
|
updateTransferStateStore(transferId, state);
|
|
861
|
-
|
|
826
|
+
|
|
862
827
|
if (__DEV__) {
|
|
863
828
|
logger('Updated transfer state', { transferId, state });
|
|
864
829
|
}
|
|
@@ -866,7 +831,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
866
831
|
|
|
867
832
|
const clearTransferCode = useCallback(async (transferId: string) => {
|
|
868
833
|
clearTransferCodeStore(transferId);
|
|
869
|
-
|
|
834
|
+
|
|
870
835
|
if (__DEV__) {
|
|
871
836
|
logger('Cleared transfer code', { transferId });
|
|
872
837
|
}
|
|
@@ -908,7 +873,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
908
873
|
const storedTransfer = getTransferCode(data.transferId);
|
|
909
874
|
|
|
910
875
|
if (!storedTransfer) {
|
|
911
|
-
logger('Transfer code not found for transferId', {
|
|
876
|
+
logger('Transfer code not found for transferId', {
|
|
912
877
|
transferId: data.transferId,
|
|
913
878
|
});
|
|
914
879
|
toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
|
|
@@ -930,7 +895,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
930
895
|
// Verify deviceId matches - very lenient since publicKey is the critical check
|
|
931
896
|
// If publicKey matches, we allow deletion even if deviceId doesn't match exactly
|
|
932
897
|
// This handles cases where deviceId might not be available or slightly different
|
|
933
|
-
const deviceIdMatches =
|
|
898
|
+
const deviceIdMatches =
|
|
934
899
|
// Exact match
|
|
935
900
|
(data.sourceDeviceId && data.sourceDeviceId === currentDeviceId) ||
|
|
936
901
|
// Stored sourceDeviceId matches current deviceId
|
|
@@ -1010,7 +975,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1010
975
|
}
|
|
1011
976
|
|
|
1012
977
|
await deleteIdentityAndClearAccount(false, false, true);
|
|
1013
|
-
|
|
978
|
+
|
|
1014
979
|
// Verify identity was actually deleted
|
|
1015
980
|
const identityDeleted = !(await KeyManager.hasIdentity());
|
|
1016
981
|
if (!identityDeleted) {
|