@oxyhq/services 5.16.22 → 5.16.24
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/core/HttpService.js +15 -79
- package/lib/commonjs/core/HttpService.js.map +1 -1
- package/lib/commonjs/core/OxyServices.base.js +3 -11
- package/lib/commonjs/core/OxyServices.base.js.map +1 -1
- package/lib/commonjs/core/services/SessionService.js +163 -0
- package/lib/commonjs/core/services/SessionService.js.map +1 -0
- package/lib/commonjs/core/services/TokenService.js +206 -0
- package/lib/commonjs/core/services/TokenService.js.map +1 -0
- package/lib/commonjs/models/interfaces.js +15 -0
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +100 -22
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +11 -3
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +228 -57
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/core/HttpService.js +15 -79
- package/lib/module/core/HttpService.js.map +1 -1
- package/lib/module/core/OxyServices.base.js +4 -11
- package/lib/module/core/OxyServices.base.js.map +1 -1
- package/lib/module/core/services/SessionService.js +159 -0
- package/lib/module/core/services/SessionService.js.map +1 -0
- package/lib/module/core/services/TokenService.js +203 -0
- package/lib/module/core/services/TokenService.js.map +1 -0
- package/lib/module/models/interfaces.js +15 -0
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +100 -22
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +11 -3
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +228 -57
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/typescript/core/HttpService.d.ts +1 -1
- package/lib/typescript/core/HttpService.d.ts.map +1 -1
- package/lib/typescript/core/OxyServices.base.d.ts +6 -0
- 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.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 +1 -3
- 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.map +1 -1
- package/lib/typescript/core/services/SessionService.d.ts +78 -0
- package/lib/typescript/core/services/SessionService.d.ts.map +1 -0
- package/lib/typescript/core/services/TokenService.d.ts +72 -0
- package/lib/typescript/core/services/TokenService.d.ts.map +1 -0
- package/lib/typescript/models/interfaces.d.ts +14 -0
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +7 -0
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts +8 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/HttpService.ts +15 -95
- package/src/core/OxyServices.base.ts +3 -20
- package/src/core/services/SessionService.ts +173 -0
- package/src/core/services/TokenService.ts +226 -0
- package/src/models/interfaces.ts +16 -2
- package/src/models/session.ts +8 -1
- package/src/ui/context/OxyContext.tsx +105 -23
- package/src/ui/context/hooks/useAuthOperations.ts +11 -3
- package/src/ui/hooks/useSessionSocket.ts +229 -55
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Contains core infrastructure, HTTP client, request management, and error handling
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import { handleHttpError } from '../utils/errorUtils';
|
|
10
10
|
import { HttpService } from './HttpService';
|
|
11
11
|
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
12
|
+
import { tokenService } from './services/TokenService';
|
|
12
13
|
/**
|
|
13
14
|
* Base class for OxyServices with core infrastructure
|
|
14
15
|
*/
|
|
@@ -114,18 +115,10 @@ export class OxyServicesBase {
|
|
|
114
115
|
|
|
115
116
|
/**
|
|
116
117
|
* Get the current user ID from the access token
|
|
118
|
+
* Returns MongoDB ObjectId (never publicKey)
|
|
117
119
|
*/
|
|
118
120
|
getCurrentUserId() {
|
|
119
|
-
|
|
120
|
-
if (!accessToken) {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
const decoded = jwtDecode(accessToken);
|
|
125
|
-
return decoded.userId || decoded.id || null;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
121
|
+
return tokenService.getUserIdFromToken();
|
|
129
122
|
}
|
|
130
123
|
|
|
131
124
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["handleHttpError","HttpService","OxyAuthenticationError","OxyAuthenticationTimeoutError","tokenService","OxyServicesBase","constructor","args","config","Error","cloudURL","httpService","__resetTokensForTests","makeRequest","method","url","data","options","request","undefined","params","getBaseURL","getClient","getMetrics","clearCache","clearCacheEntry","key","getCacheStats","getCloudURL","setTokens","accessToken","refreshToken","clearTokens","getCurrentUserId","getUserIdFromToken","hasValidToken","hasAccessToken","getAccessToken","waitForAuth","timeoutMs","startTime","performance","now","maxTime","pollInterval","Promise","resolve","setTimeout","Math","min","withAuthRetry","operation","operationName","maxRetries","retryDelay","authTimeoutMs","attempt","authReady","error","isLastAttempt","errorObj","isAuthError","response","status","code","message","includes","handleError","validate","res","cache","retry","valid","api","trim","err","details","healthCheck","timeout"],"sourceRoot":"../../../src","sources":["core/OxyServices.base.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SessionService - Single Source of Truth for Session Management
|
|
5
|
+
*
|
|
6
|
+
* Handles all session operations: creation, validation, refresh, invalidation.
|
|
7
|
+
* Manages active session state and provides session data to other services.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* - Single source of truth for session operations
|
|
11
|
+
* - Handles both online and offline sessions
|
|
12
|
+
* - Integrates with TokenService for token management
|
|
13
|
+
* - userId is always MongoDB ObjectId, never publicKey
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { tokenService } from './TokenService';
|
|
17
|
+
/**
|
|
18
|
+
* SessionService - Singleton pattern for global session management
|
|
19
|
+
*/
|
|
20
|
+
class SessionService {
|
|
21
|
+
oxyServices = null;
|
|
22
|
+
activeSession = null;
|
|
23
|
+
sessions = [];
|
|
24
|
+
constructor() {}
|
|
25
|
+
static getInstance() {
|
|
26
|
+
if (!SessionService.instance) {
|
|
27
|
+
SessionService.instance = new SessionService();
|
|
28
|
+
}
|
|
29
|
+
return SessionService.instance;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize SessionService with OxyServices instance
|
|
34
|
+
*/
|
|
35
|
+
initialize(oxyServices) {
|
|
36
|
+
this.oxyServices = oxyServices;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get active session
|
|
41
|
+
*/
|
|
42
|
+
getActiveSession() {
|
|
43
|
+
return this.activeSession;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get all sessions
|
|
48
|
+
*/
|
|
49
|
+
getAllSessions() {
|
|
50
|
+
return [...this.sessions];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a new session (sign in)
|
|
55
|
+
* @param publicKey - User's public key for authentication
|
|
56
|
+
* @returns User object and session data
|
|
57
|
+
*/
|
|
58
|
+
async createSession(publicKey) {
|
|
59
|
+
if (!this.oxyServices) {
|
|
60
|
+
throw new Error('SessionService not initialized with OxyServices');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// This will be implemented by delegating to existing sign-in logic
|
|
64
|
+
// For now, this is a placeholder that shows the interface
|
|
65
|
+
throw new Error('SessionService.createSession not yet implemented - use existing signIn flow');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Refresh current session
|
|
70
|
+
*/
|
|
71
|
+
async refreshSession() {
|
|
72
|
+
if (!this.activeSession) {
|
|
73
|
+
throw new Error('No active session to refresh');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Refresh token first
|
|
77
|
+
await tokenService.refreshTokenIfNeeded();
|
|
78
|
+
|
|
79
|
+
// Then refresh session data from server
|
|
80
|
+
// Implementation will be added
|
|
81
|
+
return this.activeSession;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Validate current session
|
|
86
|
+
*/
|
|
87
|
+
async validateSession() {
|
|
88
|
+
if (!this.activeSession) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check if session expired
|
|
93
|
+
if (new Date(this.activeSession.expiresAt) < new Date()) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if token is valid
|
|
98
|
+
const token = tokenService.getAccessToken();
|
|
99
|
+
if (!token) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Additional validation can be added here
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Invalidate current session (sign out)
|
|
109
|
+
*/
|
|
110
|
+
async invalidateSession() {
|
|
111
|
+
if (!this.activeSession || !this.oxyServices) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
// Call API to invalidate session on server
|
|
116
|
+
await this.oxyServices.makeRequest('POST', `/api/session/${this.activeSession.sessionId}/logout`, undefined, {
|
|
117
|
+
cache: false
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Continue with local cleanup even if API call fails
|
|
121
|
+
console.warn('Failed to invalidate session on server:', error);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Clear tokens
|
|
125
|
+
tokenService.clearTokens();
|
|
126
|
+
|
|
127
|
+
// Clear active session
|
|
128
|
+
this.activeSession = null;
|
|
129
|
+
this.sessions = this.sessions.filter(s => s.sessionId !== this.activeSession?.sessionId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Set active session (internal use)
|
|
134
|
+
*/
|
|
135
|
+
setActiveSession(session) {
|
|
136
|
+
this.activeSession = session;
|
|
137
|
+
|
|
138
|
+
// Update sessions list
|
|
139
|
+
const existingIndex = this.sessions.findIndex(s => s.sessionId === session.sessionId);
|
|
140
|
+
if (existingIndex >= 0) {
|
|
141
|
+
this.sessions[existingIndex] = session;
|
|
142
|
+
} else {
|
|
143
|
+
this.sessions.push(session);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Clear all sessions (logout all)
|
|
149
|
+
*/
|
|
150
|
+
clearAllSessions() {
|
|
151
|
+
this.activeSession = null;
|
|
152
|
+
this.sessions = [];
|
|
153
|
+
tokenService.clearTokens();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Export singleton instance
|
|
158
|
+
export const sessionService = SessionService.getInstance();
|
|
159
|
+
//# sourceMappingURL=SessionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["tokenService","SessionService","oxyServices","activeSession","sessions","constructor","getInstance","instance","initialize","getActiveSession","getAllSessions","createSession","publicKey","Error","refreshSession","refreshTokenIfNeeded","validateSession","Date","expiresAt","token","getAccessToken","invalidateSession","makeRequest","sessionId","undefined","cache","error","console","warn","clearTokens","filter","s","setActiveSession","session","existingIndex","findIndex","push","clearAllSessions","sessionService"],"sourceRoot":"../../../../src","sources":["core/services/SessionService.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TokenService - Single Source of Truth for Token Management
|
|
5
|
+
*
|
|
6
|
+
* Handles all token storage, retrieval, refresh, and validation.
|
|
7
|
+
* Used by HttpService, SocketService, and other services that need tokens.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* - Single storage location (no duplication)
|
|
11
|
+
* - Automatic token refresh when expiring soon
|
|
12
|
+
* - Type-safe token payload handling
|
|
13
|
+
* - userId is always MongoDB ObjectId, never publicKey
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { jwtDecode } from 'jwt-decode';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* AccessTokenPayload - Matches the token payload structure from API
|
|
20
|
+
* userId is always MongoDB ObjectId (24 hex characters), never publicKey
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* TokenService - Singleton pattern for global token management
|
|
25
|
+
*/
|
|
26
|
+
class TokenService {
|
|
27
|
+
tokenStore = {
|
|
28
|
+
accessToken: null,
|
|
29
|
+
refreshToken: null
|
|
30
|
+
};
|
|
31
|
+
refreshPromise = null;
|
|
32
|
+
baseURL = null;
|
|
33
|
+
constructor() {}
|
|
34
|
+
static getInstance() {
|
|
35
|
+
if (!TokenService.instance) {
|
|
36
|
+
TokenService.instance = new TokenService();
|
|
37
|
+
}
|
|
38
|
+
return TokenService.instance;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initialize TokenService with base URL for refresh requests
|
|
43
|
+
*/
|
|
44
|
+
initialize(baseURL) {
|
|
45
|
+
this.baseURL = baseURL;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get current access token
|
|
50
|
+
*/
|
|
51
|
+
getAccessToken() {
|
|
52
|
+
return this.tokenStore.accessToken;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get current refresh token
|
|
57
|
+
*/
|
|
58
|
+
getRefreshToken() {
|
|
59
|
+
return this.tokenStore.refreshToken;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Set tokens (called after login or token refresh)
|
|
64
|
+
*/
|
|
65
|
+
setTokens(accessToken, refreshToken = '') {
|
|
66
|
+
this.tokenStore.accessToken = accessToken;
|
|
67
|
+
this.tokenStore.refreshToken = refreshToken || this.tokenStore.refreshToken;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Clear all tokens (called on logout)
|
|
72
|
+
*/
|
|
73
|
+
clearTokens() {
|
|
74
|
+
this.tokenStore.accessToken = null;
|
|
75
|
+
this.tokenStore.refreshToken = null;
|
|
76
|
+
this.refreshPromise = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if access token exists
|
|
81
|
+
*/
|
|
82
|
+
hasAccessToken() {
|
|
83
|
+
return !!this.tokenStore.accessToken;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if token is expiring soon (within 60 seconds)
|
|
88
|
+
*/
|
|
89
|
+
isTokenExpiringSoon() {
|
|
90
|
+
const token = this.tokenStore.accessToken;
|
|
91
|
+
if (!token) return false;
|
|
92
|
+
try {
|
|
93
|
+
const decoded = jwtDecode(token);
|
|
94
|
+
if (!decoded.exp) return false;
|
|
95
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
96
|
+
return decoded.exp - currentTime < 60; // Expiring within 60 seconds
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get userId from current access token
|
|
104
|
+
* Returns MongoDB ObjectId (never publicKey)
|
|
105
|
+
*/
|
|
106
|
+
getUserIdFromToken() {
|
|
107
|
+
const token = this.tokenStore.accessToken;
|
|
108
|
+
if (!token) return null;
|
|
109
|
+
try {
|
|
110
|
+
const decoded = jwtDecode(token);
|
|
111
|
+
return decoded.userId || null;
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Refresh access token if expiring soon
|
|
119
|
+
* Returns promise that resolves when token is refreshed (or already valid)
|
|
120
|
+
*/
|
|
121
|
+
async refreshTokenIfNeeded() {
|
|
122
|
+
// If already refreshing, wait for that promise
|
|
123
|
+
if (this.refreshPromise) {
|
|
124
|
+
return this.refreshPromise;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If token not expiring soon, no refresh needed
|
|
128
|
+
if (!this.isTokenExpiringSoon()) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Start refresh
|
|
133
|
+
this.refreshPromise = this._performRefresh();
|
|
134
|
+
try {
|
|
135
|
+
await this.refreshPromise;
|
|
136
|
+
} finally {
|
|
137
|
+
this.refreshPromise = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Perform token refresh
|
|
143
|
+
*/
|
|
144
|
+
async _performRefresh() {
|
|
145
|
+
const token = this.tokenStore.accessToken;
|
|
146
|
+
if (!token) {
|
|
147
|
+
throw new Error('No access token to refresh');
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const decoded = jwtDecode(token);
|
|
151
|
+
if (!decoded.sessionId) {
|
|
152
|
+
throw new Error('Token missing sessionId');
|
|
153
|
+
}
|
|
154
|
+
if (!this.baseURL) {
|
|
155
|
+
throw new Error('TokenService not initialized with baseURL');
|
|
156
|
+
}
|
|
157
|
+
const refreshUrl = `${this.baseURL}/api/session/token/${decoded.sessionId}`;
|
|
158
|
+
const response = await fetch(refreshUrl, {
|
|
159
|
+
method: 'GET',
|
|
160
|
+
headers: {
|
|
161
|
+
'Accept': 'application/json'
|
|
162
|
+
},
|
|
163
|
+
signal: AbortSignal.timeout(5000)
|
|
164
|
+
});
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
throw new Error(`Token refresh failed: ${response.status}`);
|
|
167
|
+
}
|
|
168
|
+
const {
|
|
169
|
+
accessToken: newToken
|
|
170
|
+
} = await response.json();
|
|
171
|
+
if (!newToken) {
|
|
172
|
+
throw new Error('No access token in refresh response');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Validate new token has correct userId format (ObjectId)
|
|
176
|
+
const newDecoded = jwtDecode(newToken);
|
|
177
|
+
if (newDecoded.userId && !/^[0-9a-fA-F]{24}$/.test(newDecoded.userId)) {
|
|
178
|
+
throw new Error(`Invalid userId format in refreshed token: ${newDecoded.userId.substring(0, 20)}...`);
|
|
179
|
+
}
|
|
180
|
+
this.setTokens(newToken);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
// Clear tokens on refresh failure (likely expired or invalid)
|
|
183
|
+
this.clearTokens();
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get authorization header with automatic refresh
|
|
190
|
+
*/
|
|
191
|
+
async getAuthHeader() {
|
|
192
|
+
// Refresh if needed
|
|
193
|
+
await this.refreshTokenIfNeeded().catch(() => {
|
|
194
|
+
// Ignore refresh errors, will use current token or return null
|
|
195
|
+
});
|
|
196
|
+
const token = this.tokenStore.accessToken;
|
|
197
|
+
return token ? `Bearer ${token}` : null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Export singleton instance
|
|
202
|
+
export const tokenService = TokenService.getInstance();
|
|
203
|
+
//# sourceMappingURL=TokenService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["jwtDecode","TokenService","tokenStore","accessToken","refreshToken","refreshPromise","baseURL","constructor","getInstance","instance","initialize","getAccessToken","getRefreshToken","setTokens","clearTokens","hasAccessToken","isTokenExpiringSoon","token","decoded","exp","currentTime","Math","floor","Date","now","getUserIdFromToken","userId","refreshTokenIfNeeded","_performRefresh","Error","sessionId","refreshUrl","response","fetch","method","headers","signal","AbortSignal","timeout","ok","status","newToken","json","newDecoded","test","substring","error","getAuthHeader","catch","tokenService"],"sourceRoot":"../../../../src","sources":["core/services/TokenService.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* User Model
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT:
|
|
7
|
+
* - id: MongoDB ObjectId (24 hex characters) - PRIMARY IDENTIFIER for all internal operations
|
|
8
|
+
* - publicKey: Cryptographic public key (130 hex characters) - LOOKUP KEY for authentication and identity operations
|
|
9
|
+
*
|
|
10
|
+
* Never use publicKey as an ID. Always use id (ObjectId) for:
|
|
11
|
+
* - Database queries
|
|
12
|
+
* - Session userId
|
|
13
|
+
* - Token userId
|
|
14
|
+
* - Socket room names
|
|
15
|
+
* - API route parameters (unless explicitly doing publicKey lookup)
|
|
16
|
+
*/
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* File management interfaces
|
|
5
20
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["SECURITY_EVENT_SEVERITY_MAP"],"sourceRoot":"../../../src","sources":["models/interfaces.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"names":["SECURITY_EVENT_SEVERITY_MAP"],"sourceRoot":"../../../src","sources":["models/interfaces.ts"],"mappings":";;AAuBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAmMA;AACA;AACA;;AAmDA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAuGA;AACA;AACA;;AAqBA;AACA;AACA;;AAcA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA,OAAO,MAAMA,2BAA6E,GAAG;EAC3F,SAAS,EAAE,KAAK;EAChB,UAAU,EAAE,KAAK;EACjB,iBAAiB,EAAE,KAAK;EACxB,eAAe,EAAE,QAAQ;EACzB,cAAc,EAAE,QAAQ;EACxB,gBAAgB,EAAE,QAAQ;EAC1B,2BAA2B,EAAE,QAAQ;EACrC,kBAAkB,EAAE,MAAM;EAC1B,sBAAsB,EAAE,MAAM;EAC9B,gBAAgB,EAAE,MAAM;EACxB,qBAAqB,EAAE;AACzB,CAAC;;AAED;AACA;AACA;;AAeA;AACA;AACA;;AAkBA","ignoreList":[]}
|
|
@@ -507,7 +507,11 @@ export const OxyProvider = ({
|
|
|
507
507
|
}, [restoreSessionsFromStorage, storage]);
|
|
508
508
|
const activeSession = activeSessionId ? sessions.find(session => session.sessionId === activeSessionId) : undefined;
|
|
509
509
|
const currentDeviceId = activeSession?.deviceId ?? null;
|
|
510
|
-
|
|
510
|
+
|
|
511
|
+
// Get userId from JWT token (MongoDB ObjectId) for socket room matching
|
|
512
|
+
// user.id is set to publicKey for compatibility, but socket rooms use MongoDB ObjectId
|
|
513
|
+
// The JWT token's userId field contains the MongoDB ObjectId
|
|
514
|
+
const userId = oxyServices.getCurrentUserId() || user?.id;
|
|
511
515
|
|
|
512
516
|
// Store transfer codes in memory for verification
|
|
513
517
|
// Map: transferId -> { code, sourceDeviceId, publicKey, timestamp }
|
|
@@ -569,30 +573,47 @@ export const OxyProvider = ({
|
|
|
569
573
|
logout().catch(remoteError => logger('Failed to process remote sign out', remoteError));
|
|
570
574
|
}, [logger, logout]);
|
|
571
575
|
const handleIdentityTransferComplete = useCallback(async data => {
|
|
576
|
+
if (__DEV__) {
|
|
577
|
+
console.log('[OxyContext] handleIdentityTransferComplete called', {
|
|
578
|
+
transferId: data.transferId,
|
|
579
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
580
|
+
currentDeviceId,
|
|
581
|
+
hasActiveSession: activeSessionId !== null
|
|
582
|
+
});
|
|
583
|
+
}
|
|
572
584
|
try {
|
|
585
|
+
logger('Received identity transfer complete notification', {
|
|
586
|
+
transferId: data.transferId,
|
|
587
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
588
|
+
currentDeviceId,
|
|
589
|
+
hasActiveSession: activeSessionId !== null,
|
|
590
|
+
publicKey: data.publicKey.substring(0, 16) + '...'
|
|
591
|
+
});
|
|
592
|
+
|
|
573
593
|
// Get stored transfer code for verification
|
|
574
594
|
const storedTransfer = getTransferCode(data.transferId);
|
|
575
595
|
if (!storedTransfer) {
|
|
596
|
+
if (__DEV__) {
|
|
597
|
+
console.warn('[OxyContext] Transfer code not found for transferId', {
|
|
598
|
+
transferId: data.transferId
|
|
599
|
+
});
|
|
600
|
+
}
|
|
576
601
|
logger('Transfer code not found for transferId', {
|
|
577
602
|
transferId: data.transferId
|
|
578
603
|
});
|
|
579
604
|
toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
|
|
580
605
|
return;
|
|
581
606
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const deviceIdMatches = data.sourceDeviceId && (data.sourceDeviceId === currentDeviceId || currentDeviceId === null && activeSessionId !== null);
|
|
585
|
-
if (!deviceIdMatches) {
|
|
586
|
-
logger('Device ID mismatch for transfer', {
|
|
607
|
+
if (__DEV__) {
|
|
608
|
+
console.log('[OxyContext] Found stored transfer code', {
|
|
587
609
|
transferId: data.transferId,
|
|
588
|
-
|
|
610
|
+
storedSourceDeviceId: storedTransfer.sourceDeviceId,
|
|
611
|
+
receivedSourceDeviceId: data.sourceDeviceId,
|
|
589
612
|
currentDeviceId
|
|
590
613
|
});
|
|
591
|
-
toast.error('Transfer verification failed: Device mismatch. Identity will not be deleted.');
|
|
592
|
-
return;
|
|
593
614
|
}
|
|
594
615
|
|
|
595
|
-
// Verify publicKey matches
|
|
616
|
+
// Verify publicKey matches first (most important check)
|
|
596
617
|
const publicKeyMatches = data.publicKey === storedTransfer.publicKey;
|
|
597
618
|
if (!publicKeyMatches) {
|
|
598
619
|
logger('Public key mismatch for transfer', {
|
|
@@ -604,21 +625,53 @@ export const OxyProvider = ({
|
|
|
604
625
|
return;
|
|
605
626
|
}
|
|
606
627
|
|
|
628
|
+
// Verify deviceId matches - very lenient since publicKey is the critical check
|
|
629
|
+
// If publicKey matches, we allow deletion even if deviceId doesn't match exactly
|
|
630
|
+
// This handles cases where deviceId might not be available or slightly different
|
|
631
|
+
const deviceIdMatches =
|
|
632
|
+
// Exact match
|
|
633
|
+
data.sourceDeviceId && data.sourceDeviceId === currentDeviceId ||
|
|
634
|
+
// Stored sourceDeviceId matches current deviceId
|
|
635
|
+
storedTransfer.sourceDeviceId && storedTransfer.sourceDeviceId === currentDeviceId;
|
|
636
|
+
|
|
637
|
+
// If publicKey matches, we're very lenient with deviceId - only warn but don't block
|
|
638
|
+
if (!deviceIdMatches && publicKeyMatches) {
|
|
639
|
+
logger('Device ID mismatch for transfer, but publicKey matches - proceeding with deletion', {
|
|
640
|
+
transferId: data.transferId,
|
|
641
|
+
receivedDeviceId: data.sourceDeviceId,
|
|
642
|
+
storedDeviceId: storedTransfer.sourceDeviceId,
|
|
643
|
+
currentDeviceId,
|
|
644
|
+
hasActiveSession: activeSessionId !== null
|
|
645
|
+
});
|
|
646
|
+
// Proceed with deletion - publicKey match is the critical verification
|
|
647
|
+
} else if (!deviceIdMatches && !publicKeyMatches) {
|
|
648
|
+
// Both don't match - this is suspicious, block deletion
|
|
649
|
+
logger('Device ID and publicKey mismatch for transfer', {
|
|
650
|
+
transferId: data.transferId,
|
|
651
|
+
receivedDeviceId: data.sourceDeviceId,
|
|
652
|
+
currentDeviceId
|
|
653
|
+
});
|
|
654
|
+
toast.error('Transfer verification failed: Device and key mismatch. Identity will not be deleted.');
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
607
658
|
// Verify transfer code matches (if provided)
|
|
659
|
+
// Transfer code is optional - if not provided, we still proceed if publicKey matches
|
|
608
660
|
if (data.transferCode) {
|
|
609
661
|
const codeMatches = data.transferCode.toUpperCase() === storedTransfer.code.toUpperCase();
|
|
610
662
|
if (!codeMatches) {
|
|
611
|
-
logger('Transfer code mismatch', {
|
|
663
|
+
logger('Transfer code mismatch, but publicKey matches - proceeding with deletion', {
|
|
612
664
|
transferId: data.transferId,
|
|
613
|
-
receivedCode: data.transferCode
|
|
665
|
+
receivedCode: data.transferCode,
|
|
666
|
+
storedCode: storedTransfer.code.substring(0, 2) + '****'
|
|
614
667
|
});
|
|
615
|
-
|
|
616
|
-
|
|
668
|
+
// Don't block - publicKey match is sufficient, code mismatch might be due to user error
|
|
669
|
+
// Log warning but proceed
|
|
617
670
|
}
|
|
618
671
|
} else {
|
|
619
|
-
// If transfer code is not provided, log
|
|
672
|
+
// If transfer code is not provided, log info but proceed
|
|
620
673
|
if (__DEV__) {
|
|
621
|
-
logger('Transfer code not provided in completion notification', {
|
|
674
|
+
logger('Transfer code not provided in completion notification, but publicKey matches - proceeding', {
|
|
622
675
|
transferId: data.transferId
|
|
623
676
|
});
|
|
624
677
|
}
|
|
@@ -630,7 +683,8 @@ export const OxyProvider = ({
|
|
|
630
683
|
if (transferAge > tenMinutes) {
|
|
631
684
|
logger('Transfer confirmation received too late', {
|
|
632
685
|
transferId: data.transferId,
|
|
633
|
-
age: transferAge
|
|
686
|
+
age: transferAge,
|
|
687
|
+
ageMinutes: Math.round(transferAge / 60000)
|
|
634
688
|
});
|
|
635
689
|
toast.error('Transfer verification failed: Confirmation received too late. Identity will not be deleted.');
|
|
636
690
|
clearTransferCode(data.transferId);
|
|
@@ -638,15 +692,37 @@ export const OxyProvider = ({
|
|
|
638
692
|
}
|
|
639
693
|
|
|
640
694
|
// All verifications passed - automatically delete identity
|
|
695
|
+
if (__DEV__) {
|
|
696
|
+
console.log('[OxyContext] All verifications passed, deleting identity', {
|
|
697
|
+
transferId: data.transferId,
|
|
698
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
699
|
+
currentDeviceId
|
|
700
|
+
});
|
|
701
|
+
}
|
|
641
702
|
logger('All transfer verifications passed, deleting identity from source device', {
|
|
642
703
|
transferId: data.transferId,
|
|
643
|
-
sourceDeviceId: data.sourceDeviceId
|
|
704
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
705
|
+
publicKey: data.publicKey.substring(0, 16) + '...'
|
|
644
706
|
});
|
|
645
|
-
|
|
707
|
+
try {
|
|
708
|
+
await deleteIdentityAndClearAccount(false, false, true);
|
|
709
|
+
if (__DEV__) {
|
|
710
|
+
console.log('[OxyContext] Identity deleted successfully');
|
|
711
|
+
}
|
|
646
712
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
713
|
+
// Clear the transfer code after successful deletion
|
|
714
|
+
clearTransferCode(data.transferId);
|
|
715
|
+
logger('Identity successfully deleted and transfer code cleared', {
|
|
716
|
+
transferId: data.transferId
|
|
717
|
+
});
|
|
718
|
+
toast.success('Identity successfully transferred and removed from this device');
|
|
719
|
+
} catch (deleteError) {
|
|
720
|
+
if (__DEV__) {
|
|
721
|
+
console.error('[OxyContext] Error deleting identity', deleteError);
|
|
722
|
+
}
|
|
723
|
+
logger('Error during identity deletion', deleteError);
|
|
724
|
+
throw deleteError; // Re-throw to be caught by outer catch
|
|
725
|
+
}
|
|
650
726
|
} catch (error) {
|
|
651
727
|
logger('Failed to delete identity after transfer', error);
|
|
652
728
|
toast.error(error?.message || 'Failed to remove identity from this device. Please try again manually from Security Settings.');
|
|
@@ -660,6 +736,8 @@ export const OxyProvider = ({
|
|
|
660
736
|
logout,
|
|
661
737
|
clearSessionState,
|
|
662
738
|
baseURL: oxyServices.getBaseURL(),
|
|
739
|
+
getAccessToken: () => oxyServices.getAccessToken(),
|
|
740
|
+
getTransferCode: getTransferCode,
|
|
663
741
|
onRemoteSignOut: handleRemoteSignOut,
|
|
664
742
|
onSessionRemoved: handleSessionRemoved,
|
|
665
743
|
onIdentityTransferComplete: handleIdentityTransferComplete
|