@oxyhq/core 1.0.0
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 +50 -0
- package/dist/cjs/AuthManager.js +361 -0
- package/dist/cjs/CrossDomainAuth.js +258 -0
- package/dist/cjs/HttpService.js +618 -0
- package/dist/cjs/OxyServices.base.js +263 -0
- package/dist/cjs/OxyServices.errors.js +22 -0
- package/dist/cjs/OxyServices.js +63 -0
- package/dist/cjs/constants/version.js +16 -0
- package/dist/cjs/crypto/index.js +20 -0
- package/dist/cjs/crypto/keyManager.js +887 -0
- package/dist/cjs/crypto/polyfill.js +64 -0
- package/dist/cjs/crypto/recoveryPhrase.js +169 -0
- package/dist/cjs/crypto/signatureService.js +296 -0
- package/dist/cjs/i18n/index.js +73 -0
- package/dist/cjs/i18n/locales/ar-SA.json +120 -0
- package/dist/cjs/i18n/locales/ca-ES.json +120 -0
- package/dist/cjs/i18n/locales/de-DE.json +120 -0
- package/dist/cjs/i18n/locales/en-US.json +956 -0
- package/dist/cjs/i18n/locales/es-ES.json +944 -0
- package/dist/cjs/i18n/locales/fr-FR.json +120 -0
- package/dist/cjs/i18n/locales/it-IT.json +120 -0
- package/dist/cjs/i18n/locales/ja-JP.json +119 -0
- package/dist/cjs/i18n/locales/ko-KR.json +120 -0
- package/dist/cjs/i18n/locales/locales/ar-SA.json +120 -0
- package/dist/cjs/i18n/locales/locales/ca-ES.json +120 -0
- package/dist/cjs/i18n/locales/locales/de-DE.json +120 -0
- package/dist/cjs/i18n/locales/locales/en-US.json +956 -0
- package/dist/cjs/i18n/locales/locales/es-ES.json +944 -0
- package/dist/cjs/i18n/locales/locales/fr-FR.json +120 -0
- package/dist/cjs/i18n/locales/locales/it-IT.json +120 -0
- package/dist/cjs/i18n/locales/locales/ja-JP.json +119 -0
- package/dist/cjs/i18n/locales/locales/ko-KR.json +120 -0
- package/dist/cjs/i18n/locales/locales/pt-PT.json +120 -0
- package/dist/cjs/i18n/locales/locales/zh-CN.json +120 -0
- package/dist/cjs/i18n/locales/pt-PT.json +120 -0
- package/dist/cjs/i18n/locales/zh-CN.json +120 -0
- package/dist/cjs/index.js +153 -0
- package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
- package/dist/cjs/mixins/OxyServices.assets.js +380 -0
- package/dist/cjs/mixins/OxyServices.auth.js +259 -0
- package/dist/cjs/mixins/OxyServices.developer.js +97 -0
- package/dist/cjs/mixins/OxyServices.devices.js +116 -0
- package/dist/cjs/mixins/OxyServices.features.js +309 -0
- package/dist/cjs/mixins/OxyServices.fedcm.js +435 -0
- package/dist/cjs/mixins/OxyServices.karma.js +108 -0
- package/dist/cjs/mixins/OxyServices.language.js +154 -0
- package/dist/cjs/mixins/OxyServices.location.js +43 -0
- package/dist/cjs/mixins/OxyServices.payment.js +158 -0
- package/dist/cjs/mixins/OxyServices.popup.js +371 -0
- package/dist/cjs/mixins/OxyServices.privacy.js +162 -0
- package/dist/cjs/mixins/OxyServices.redirect.js +345 -0
- package/dist/cjs/mixins/OxyServices.security.js +81 -0
- package/dist/cjs/mixins/OxyServices.user.js +355 -0
- package/dist/cjs/mixins/OxyServices.utility.js +156 -0
- package/dist/cjs/mixins/index.js +79 -0
- package/dist/cjs/mixins/mixinHelpers.js +53 -0
- package/dist/cjs/models/interfaces.js +20 -0
- package/dist/cjs/models/session.js +2 -0
- package/dist/cjs/shared/index.js +70 -0
- package/dist/cjs/shared/utils/colorUtils.js +153 -0
- package/dist/cjs/shared/utils/debugUtils.js +73 -0
- package/dist/cjs/shared/utils/errorUtils.js +183 -0
- package/dist/cjs/shared/utils/index.js +49 -0
- package/dist/cjs/shared/utils/networkUtils.js +183 -0
- package/dist/cjs/shared/utils/themeUtils.js +106 -0
- package/dist/cjs/utils/apiUtils.js +61 -0
- package/dist/cjs/utils/asyncUtils.js +194 -0
- package/dist/cjs/utils/cache.js +226 -0
- package/dist/cjs/utils/deviceManager.js +205 -0
- package/dist/cjs/utils/errorUtils.js +154 -0
- package/dist/cjs/utils/index.js +26 -0
- package/dist/cjs/utils/languageUtils.js +165 -0
- package/dist/cjs/utils/loggerUtils.js +126 -0
- package/dist/cjs/utils/platform.js +144 -0
- package/dist/cjs/utils/requestUtils.js +209 -0
- package/dist/cjs/utils/sessionUtils.js +181 -0
- package/dist/cjs/utils/validationUtils.js +173 -0
- package/dist/esm/AuthManager.js +356 -0
- package/dist/esm/CrossDomainAuth.js +253 -0
- package/dist/esm/HttpService.js +614 -0
- package/dist/esm/OxyServices.base.js +259 -0
- package/dist/esm/OxyServices.errors.js +17 -0
- package/dist/esm/OxyServices.js +59 -0
- package/dist/esm/constants/version.js +13 -0
- package/dist/esm/crypto/index.js +13 -0
- package/dist/esm/crypto/keyManager.js +850 -0
- package/dist/esm/crypto/polyfill.js +61 -0
- package/dist/esm/crypto/recoveryPhrase.js +132 -0
- package/dist/esm/crypto/signatureService.js +259 -0
- package/dist/esm/i18n/index.js +69 -0
- package/dist/esm/i18n/locales/ar-SA.json +120 -0
- package/dist/esm/i18n/locales/ca-ES.json +120 -0
- package/dist/esm/i18n/locales/de-DE.json +120 -0
- package/dist/esm/i18n/locales/en-US.json +956 -0
- package/dist/esm/i18n/locales/es-ES.json +944 -0
- package/dist/esm/i18n/locales/fr-FR.json +120 -0
- package/dist/esm/i18n/locales/it-IT.json +120 -0
- package/dist/esm/i18n/locales/ja-JP.json +119 -0
- package/dist/esm/i18n/locales/ko-KR.json +120 -0
- package/dist/esm/i18n/locales/locales/ar-SA.json +120 -0
- package/dist/esm/i18n/locales/locales/ca-ES.json +120 -0
- package/dist/esm/i18n/locales/locales/de-DE.json +120 -0
- package/dist/esm/i18n/locales/locales/en-US.json +956 -0
- package/dist/esm/i18n/locales/locales/es-ES.json +944 -0
- package/dist/esm/i18n/locales/locales/fr-FR.json +120 -0
- package/dist/esm/i18n/locales/locales/it-IT.json +120 -0
- package/dist/esm/i18n/locales/locales/ja-JP.json +119 -0
- package/dist/esm/i18n/locales/locales/ko-KR.json +120 -0
- package/dist/esm/i18n/locales/locales/pt-PT.json +120 -0
- package/dist/esm/i18n/locales/locales/zh-CN.json +120 -0
- package/dist/esm/i18n/locales/pt-PT.json +120 -0
- package/dist/esm/i18n/locales/zh-CN.json +120 -0
- package/dist/esm/index.js +55 -0
- package/dist/esm/mixins/OxyServices.analytics.js +46 -0
- package/dist/esm/mixins/OxyServices.assets.js +377 -0
- package/dist/esm/mixins/OxyServices.auth.js +256 -0
- package/dist/esm/mixins/OxyServices.developer.js +94 -0
- package/dist/esm/mixins/OxyServices.devices.js +113 -0
- package/dist/esm/mixins/OxyServices.features.js +306 -0
- package/dist/esm/mixins/OxyServices.fedcm.js +433 -0
- package/dist/esm/mixins/OxyServices.karma.js +105 -0
- package/dist/esm/mixins/OxyServices.language.js +118 -0
- package/dist/esm/mixins/OxyServices.location.js +40 -0
- package/dist/esm/mixins/OxyServices.payment.js +155 -0
- package/dist/esm/mixins/OxyServices.popup.js +369 -0
- package/dist/esm/mixins/OxyServices.privacy.js +159 -0
- package/dist/esm/mixins/OxyServices.redirect.js +343 -0
- package/dist/esm/mixins/OxyServices.security.js +78 -0
- package/dist/esm/mixins/OxyServices.user.js +352 -0
- package/dist/esm/mixins/OxyServices.utility.js +153 -0
- package/dist/esm/mixins/index.js +76 -0
- package/dist/esm/mixins/mixinHelpers.js +48 -0
- package/dist/esm/models/interfaces.js +17 -0
- package/dist/esm/models/session.js +1 -0
- package/dist/esm/shared/index.js +31 -0
- package/dist/esm/shared/utils/colorUtils.js +143 -0
- package/dist/esm/shared/utils/debugUtils.js +65 -0
- package/dist/esm/shared/utils/errorUtils.js +170 -0
- package/dist/esm/shared/utils/index.js +15 -0
- package/dist/esm/shared/utils/networkUtils.js +173 -0
- package/dist/esm/shared/utils/themeUtils.js +98 -0
- package/dist/esm/utils/apiUtils.js +55 -0
- package/dist/esm/utils/asyncUtils.js +179 -0
- package/dist/esm/utils/cache.js +218 -0
- package/dist/esm/utils/deviceManager.js +168 -0
- package/dist/esm/utils/errorUtils.js +146 -0
- package/dist/esm/utils/index.js +7 -0
- package/dist/esm/utils/languageUtils.js +158 -0
- package/dist/esm/utils/loggerUtils.js +115 -0
- package/dist/esm/utils/platform.js +102 -0
- package/dist/esm/utils/requestUtils.js +203 -0
- package/dist/esm/utils/sessionUtils.js +171 -0
- package/dist/esm/utils/validationUtils.js +153 -0
- package/dist/types/AuthManager.d.ts +143 -0
- package/dist/types/CrossDomainAuth.d.ts +160 -0
- package/dist/types/HttpService.d.ts +163 -0
- package/dist/types/OxyServices.base.d.ts +126 -0
- package/dist/types/OxyServices.d.ts +81 -0
- package/dist/types/OxyServices.errors.d.ts +11 -0
- package/dist/types/constants/version.d.ts +13 -0
- package/dist/types/crypto/index.d.ts +11 -0
- package/dist/types/crypto/keyManager.d.ts +189 -0
- package/dist/types/crypto/polyfill.d.ts +11 -0
- package/dist/types/crypto/recoveryPhrase.d.ts +58 -0
- package/dist/types/crypto/signatureService.d.ts +86 -0
- package/dist/types/i18n/index.d.ts +3 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +66 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +135 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +186 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +99 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +96 -0
- package/dist/types/mixins/OxyServices.features.d.ts +228 -0
- package/dist/types/mixins/OxyServices.fedcm.d.ts +200 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +85 -0
- package/dist/types/mixins/OxyServices.language.d.ts +81 -0
- package/dist/types/mixins/OxyServices.location.d.ts +64 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +111 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +205 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +122 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +245 -0
- package/dist/types/mixins/OxyServices.security.d.ts +78 -0
- package/dist/types/mixins/OxyServices.user.d.ts +182 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +93 -0
- package/dist/types/mixins/index.d.ts +30 -0
- package/dist/types/mixins/mixinHelpers.d.ts +31 -0
- package/dist/types/models/interfaces.d.ts +415 -0
- package/dist/types/models/session.d.ts +27 -0
- package/dist/types/shared/index.d.ts +28 -0
- package/dist/types/shared/utils/colorUtils.d.ts +104 -0
- package/dist/types/shared/utils/debugUtils.d.ts +48 -0
- package/dist/types/shared/utils/errorUtils.d.ts +97 -0
- package/dist/types/shared/utils/index.d.ts +13 -0
- package/dist/types/shared/utils/networkUtils.d.ts +139 -0
- package/dist/types/shared/utils/themeUtils.d.ts +90 -0
- package/dist/types/utils/apiUtils.d.ts +53 -0
- package/dist/types/utils/asyncUtils.d.ts +58 -0
- package/dist/types/utils/cache.d.ts +127 -0
- package/dist/types/utils/deviceManager.d.ts +65 -0
- package/dist/types/utils/errorUtils.d.ts +46 -0
- package/dist/types/utils/index.d.ts +6 -0
- package/dist/types/utils/languageUtils.d.ts +37 -0
- package/dist/types/utils/loggerUtils.d.ts +48 -0
- package/dist/types/utils/platform.d.ts +40 -0
- package/dist/types/utils/requestUtils.d.ts +123 -0
- package/dist/types/utils/sessionUtils.d.ts +54 -0
- package/dist/types/utils/validationUtils.d.ts +85 -0
- package/package.json +84 -0
- package/src/AuthManager.ts +436 -0
- package/src/CrossDomainAuth.ts +307 -0
- package/src/HttpService.ts +752 -0
- package/src/OxyServices.base.ts +334 -0
- package/src/OxyServices.errors.ts +26 -0
- package/src/OxyServices.ts +129 -0
- package/src/constants/version.ts +15 -0
- package/src/crypto/index.ts +25 -0
- package/src/crypto/keyManager.ts +962 -0
- package/src/crypto/polyfill.ts +70 -0
- package/src/crypto/recoveryPhrase.ts +166 -0
- package/src/crypto/signatureService.ts +323 -0
- package/src/i18n/index.ts +75 -0
- package/src/i18n/locales/ar-SA.json +120 -0
- package/src/i18n/locales/ca-ES.json +120 -0
- package/src/i18n/locales/de-DE.json +120 -0
- package/src/i18n/locales/en-US.json +956 -0
- package/src/i18n/locales/es-ES.json +944 -0
- package/src/i18n/locales/fr-FR.json +120 -0
- package/src/i18n/locales/it-IT.json +120 -0
- package/src/i18n/locales/ja-JP.json +119 -0
- package/src/i18n/locales/ko-KR.json +120 -0
- package/src/i18n/locales/pt-PT.json +120 -0
- package/src/i18n/locales/zh-CN.json +120 -0
- package/src/index.ts +153 -0
- package/src/mixins/OxyServices.analytics.ts +53 -0
- package/src/mixins/OxyServices.assets.ts +412 -0
- package/src/mixins/OxyServices.auth.ts +358 -0
- package/src/mixins/OxyServices.developer.ts +114 -0
- package/src/mixins/OxyServices.devices.ts +119 -0
- package/src/mixins/OxyServices.features.ts +428 -0
- package/src/mixins/OxyServices.fedcm.ts +494 -0
- package/src/mixins/OxyServices.karma.ts +111 -0
- package/src/mixins/OxyServices.language.ts +127 -0
- package/src/mixins/OxyServices.location.ts +46 -0
- package/src/mixins/OxyServices.payment.ts +163 -0
- package/src/mixins/OxyServices.popup.ts +443 -0
- package/src/mixins/OxyServices.privacy.ts +182 -0
- package/src/mixins/OxyServices.redirect.ts +397 -0
- package/src/mixins/OxyServices.security.ts +103 -0
- package/src/mixins/OxyServices.user.ts +392 -0
- package/src/mixins/OxyServices.utility.ts +191 -0
- package/src/mixins/index.ts +91 -0
- package/src/mixins/mixinHelpers.ts +69 -0
- package/src/models/interfaces.ts +511 -0
- package/src/models/session.ts +30 -0
- package/src/shared/index.ts +82 -0
- package/src/shared/utils/colorUtils.ts +155 -0
- package/src/shared/utils/debugUtils.ts +73 -0
- package/src/shared/utils/errorUtils.ts +181 -0
- package/src/shared/utils/index.ts +59 -0
- package/src/shared/utils/networkUtils.ts +248 -0
- package/src/shared/utils/themeUtils.ts +115 -0
- package/src/types/bip39.d.ts +32 -0
- package/src/types/buffer.d.ts +97 -0
- package/src/types/color.d.ts +20 -0
- package/src/types/elliptic.d.ts +62 -0
- package/src/utils/apiUtils.ts +88 -0
- package/src/utils/asyncUtils.ts +252 -0
- package/src/utils/cache.ts +264 -0
- package/src/utils/deviceManager.ts +198 -0
- package/src/utils/errorUtils.ts +216 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/languageUtils.ts +174 -0
- package/src/utils/loggerUtils.ts +153 -0
- package/src/utils/platform.ts +117 -0
- package/src/utils/requestUtils.ts +237 -0
- package/src/utils/sessionUtils.ts +206 -0
- package/src/utils/validationUtils.ts +174 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session management utilities
|
|
4
|
+
*
|
|
5
|
+
* Provides consistent session normalization, deduplication, and sorting
|
|
6
|
+
* to ensure sessions are always displayed in a predictable order.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.normalizeSession = normalizeSession;
|
|
10
|
+
exports.sessionsEqual = sessionsEqual;
|
|
11
|
+
exports.sortSessions = sortSessions;
|
|
12
|
+
exports.deduplicateSessions = deduplicateSessions;
|
|
13
|
+
exports.deduplicateSessionsByUserId = deduplicateSessionsByUserId;
|
|
14
|
+
exports.normalizeAndSortSessions = normalizeAndSortSessions;
|
|
15
|
+
exports.mergeSessions = mergeSessions;
|
|
16
|
+
exports.sessionsArraysEqual = sessionsArraysEqual;
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a session to ensure all required fields are present
|
|
19
|
+
*/
|
|
20
|
+
function normalizeSession(session) {
|
|
21
|
+
const now = new Date().toISOString();
|
|
22
|
+
return {
|
|
23
|
+
sessionId: session.sessionId,
|
|
24
|
+
deviceId: session.deviceId || '',
|
|
25
|
+
expiresAt: session.expiresAt || now,
|
|
26
|
+
lastActive: session.lastActive || now,
|
|
27
|
+
userId: session.userId || '',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Compare two sessions for equality
|
|
32
|
+
*/
|
|
33
|
+
function sessionsEqual(a, b) {
|
|
34
|
+
return a.sessionId === b.sessionId;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sort sessions by lastActive (most recent first), then by sessionId for stability
|
|
38
|
+
*/
|
|
39
|
+
function sortSessions(sessions) {
|
|
40
|
+
return [...sessions].sort((a, b) => {
|
|
41
|
+
// Sort by lastActive descending (most recent first)
|
|
42
|
+
const timeA = new Date(a.lastActive).getTime();
|
|
43
|
+
const timeB = new Date(b.lastActive).getTime();
|
|
44
|
+
if (timeA !== timeB) {
|
|
45
|
+
return timeB - timeA; // Descending order
|
|
46
|
+
}
|
|
47
|
+
// If lastActive is the same, sort by sessionId for stability
|
|
48
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Deduplicate sessions by sessionId, keeping the most recent version
|
|
53
|
+
*/
|
|
54
|
+
function deduplicateSessions(sessions) {
|
|
55
|
+
const sessionMap = new Map();
|
|
56
|
+
for (const session of sessions) {
|
|
57
|
+
const existing = sessionMap.get(session.sessionId);
|
|
58
|
+
if (!existing) {
|
|
59
|
+
sessionMap.set(session.sessionId, session);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Keep the one with more recent lastActive
|
|
63
|
+
const existingTime = new Date(existing.lastActive).getTime();
|
|
64
|
+
const currentTime = new Date(session.lastActive).getTime();
|
|
65
|
+
if (currentTime > existingTime) {
|
|
66
|
+
sessionMap.set(session.sessionId, session);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return Array.from(sessionMap.values());
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Deduplicate sessions by userId, keeping only one session per user
|
|
74
|
+
* Priority: 1) Active session (if provided), 2) Most recent session
|
|
75
|
+
* This prevents showing duplicate accounts for the same user
|
|
76
|
+
*/
|
|
77
|
+
function deduplicateSessionsByUserId(sessions, activeSessionId) {
|
|
78
|
+
if (!sessions.length)
|
|
79
|
+
return [];
|
|
80
|
+
const userSessionMap = new Map();
|
|
81
|
+
for (const session of sessions) {
|
|
82
|
+
if (!session.userId)
|
|
83
|
+
continue; // Skip sessions without userId
|
|
84
|
+
const existing = userSessionMap.get(session.userId);
|
|
85
|
+
if (!existing) {
|
|
86
|
+
userSessionMap.set(session.userId, session);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Prioritize active session
|
|
90
|
+
const isCurrentActive = activeSessionId && session.sessionId === activeSessionId;
|
|
91
|
+
const isExistingActive = activeSessionId && existing.sessionId === activeSessionId;
|
|
92
|
+
if (isCurrentActive && !isExistingActive) {
|
|
93
|
+
userSessionMap.set(session.userId, session);
|
|
94
|
+
}
|
|
95
|
+
else if (!isCurrentActive && isExistingActive) {
|
|
96
|
+
// Keep existing (active) session
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Neither is active, keep the one with more recent lastActive
|
|
101
|
+
const existingTime = new Date(existing.lastActive).getTime();
|
|
102
|
+
const currentTime = new Date(session.lastActive).getTime();
|
|
103
|
+
if (currentTime > existingTime) {
|
|
104
|
+
userSessionMap.set(session.userId, session);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Array.from(userSessionMap.values());
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Normalize, deduplicate, and sort sessions
|
|
113
|
+
* This ensures consistent session ordering across the application
|
|
114
|
+
*
|
|
115
|
+
* @param sessions - Array of sessions to normalize
|
|
116
|
+
* @param activeSessionId - Optional active session ID to prioritize
|
|
117
|
+
* @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
|
|
118
|
+
*/
|
|
119
|
+
function normalizeAndSortSessions(sessions, activeSessionId, deduplicateByUserId = true) {
|
|
120
|
+
if (!sessions.length)
|
|
121
|
+
return [];
|
|
122
|
+
// Normalize all sessions
|
|
123
|
+
const normalized = sessions.map(normalizeSession);
|
|
124
|
+
// First deduplicate by sessionId (exact duplicates)
|
|
125
|
+
const deduplicatedBySessionId = deduplicateSessions(normalized);
|
|
126
|
+
// Then deduplicate by userId if requested (one account per user)
|
|
127
|
+
const finalSessions = deduplicateByUserId
|
|
128
|
+
? deduplicateSessionsByUserId(deduplicatedBySessionId, activeSessionId)
|
|
129
|
+
: deduplicatedBySessionId;
|
|
130
|
+
// Sort consistently
|
|
131
|
+
return sortSessions(finalSessions);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Merge two session arrays, prioritizing newer data
|
|
135
|
+
* Returns normalized, deduplicated, and sorted sessions
|
|
136
|
+
*
|
|
137
|
+
* @param existing - Existing sessions array
|
|
138
|
+
* @param incoming - New sessions to merge in
|
|
139
|
+
* @param activeSessionId - Optional active session ID to prioritize
|
|
140
|
+
* @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
|
|
141
|
+
*/
|
|
142
|
+
function mergeSessions(existing, incoming, activeSessionId, deduplicateByUserId = true) {
|
|
143
|
+
if (!existing.length && !incoming.length)
|
|
144
|
+
return [];
|
|
145
|
+
if (!existing.length)
|
|
146
|
+
return normalizeAndSortSessions(incoming, activeSessionId, deduplicateByUserId);
|
|
147
|
+
if (!incoming.length)
|
|
148
|
+
return normalizeAndSortSessions(existing, activeSessionId, deduplicateByUserId);
|
|
149
|
+
// Normalize both arrays
|
|
150
|
+
const normalizedExisting = existing.map(normalizeSession);
|
|
151
|
+
const normalizedIncoming = incoming.map(normalizeSession);
|
|
152
|
+
// Create a map with existing sessions (by sessionId)
|
|
153
|
+
const sessionMap = new Map();
|
|
154
|
+
// Add existing sessions first
|
|
155
|
+
for (const session of normalizedExisting) {
|
|
156
|
+
sessionMap.set(session.sessionId, session);
|
|
157
|
+
}
|
|
158
|
+
// Merge incoming sessions - backend data always replaces existing
|
|
159
|
+
for (const session of normalizedIncoming) {
|
|
160
|
+
sessionMap.set(session.sessionId, session);
|
|
161
|
+
}
|
|
162
|
+
// Convert to array
|
|
163
|
+
const merged = Array.from(sessionMap.values());
|
|
164
|
+
// Apply userId deduplication if requested
|
|
165
|
+
const finalSessions = deduplicateByUserId
|
|
166
|
+
? deduplicateSessionsByUserId(merged, activeSessionId)
|
|
167
|
+
: merged;
|
|
168
|
+
// Sort consistently
|
|
169
|
+
return sortSessions(finalSessions);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if two session arrays are equal (same sessionIds in same order)
|
|
173
|
+
*/
|
|
174
|
+
function sessionsArraysEqual(a, b) {
|
|
175
|
+
if (a.length !== b.length) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const sortedA = sortSessions(a);
|
|
179
|
+
const sortedB = sortSessions(b);
|
|
180
|
+
return sortedA.every((session, index) => sessionsEqual(session, sortedB[index]));
|
|
181
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validation utilities for common data validation patterns
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PASSWORD_REGEX = exports.USERNAME_REGEX = exports.EMAIL_REGEX = void 0;
|
|
7
|
+
exports.isValidEmail = isValidEmail;
|
|
8
|
+
exports.isValidUsername = isValidUsername;
|
|
9
|
+
exports.isValidPassword = isValidPassword;
|
|
10
|
+
exports.isRequiredString = isRequiredString;
|
|
11
|
+
exports.isRequiredNumber = isRequiredNumber;
|
|
12
|
+
exports.isRequiredBoolean = isRequiredBoolean;
|
|
13
|
+
exports.isValidArray = isValidArray;
|
|
14
|
+
exports.isValidObject = isValidObject;
|
|
15
|
+
exports.isValidUUID = isValidUUID;
|
|
16
|
+
exports.isValidURL = isValidURL;
|
|
17
|
+
exports.isValidDate = isValidDate;
|
|
18
|
+
exports.isValidFileSize = isValidFileSize;
|
|
19
|
+
exports.isValidFileType = isValidFileType;
|
|
20
|
+
exports.sanitizeString = sanitizeString;
|
|
21
|
+
exports.sanitizeHTML = sanitizeHTML;
|
|
22
|
+
exports.isValidObjectId = isValidObjectId;
|
|
23
|
+
exports.validateAndSanitizeUserInput = validateAndSanitizeUserInput;
|
|
24
|
+
/**
|
|
25
|
+
* Email validation regex
|
|
26
|
+
*/
|
|
27
|
+
exports.EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
28
|
+
/**
|
|
29
|
+
* Username validation regex (alphanumeric, underscores, and hyphens, 3-30 chars)
|
|
30
|
+
*/
|
|
31
|
+
exports.USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,30}$/;
|
|
32
|
+
/**
|
|
33
|
+
* Password validation regex (at least 8 chars, 1 uppercase, 1 lowercase, 1 number)
|
|
34
|
+
*/
|
|
35
|
+
// At least 8 characters (tests expect len>=8 without complexity requirements)
|
|
36
|
+
exports.PASSWORD_REGEX = /^.{8,}$/;
|
|
37
|
+
/**
|
|
38
|
+
* Validate email format
|
|
39
|
+
*/
|
|
40
|
+
function isValidEmail(email) {
|
|
41
|
+
return exports.EMAIL_REGEX.test(email);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate username format
|
|
45
|
+
*/
|
|
46
|
+
function isValidUsername(username) {
|
|
47
|
+
return exports.USERNAME_REGEX.test(username);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate password strength
|
|
51
|
+
*/
|
|
52
|
+
function isValidPassword(password) {
|
|
53
|
+
return exports.PASSWORD_REGEX.test(password);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate required string
|
|
57
|
+
*/
|
|
58
|
+
function isRequiredString(value) {
|
|
59
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate required number
|
|
63
|
+
*/
|
|
64
|
+
function isRequiredNumber(value) {
|
|
65
|
+
return typeof value === 'number' && !Number.isNaN(value);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate required boolean
|
|
69
|
+
*/
|
|
70
|
+
function isRequiredBoolean(value) {
|
|
71
|
+
return typeof value === 'boolean';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate array
|
|
75
|
+
*/
|
|
76
|
+
function isValidArray(value) {
|
|
77
|
+
return Array.isArray(value);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate object
|
|
81
|
+
*/
|
|
82
|
+
function isValidObject(value) {
|
|
83
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate UUID format
|
|
87
|
+
*/
|
|
88
|
+
function isValidUUID(uuid) {
|
|
89
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
90
|
+
return UUID_REGEX.test(uuid);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate URL format
|
|
94
|
+
*/
|
|
95
|
+
function isValidURL(url) {
|
|
96
|
+
try {
|
|
97
|
+
new URL(url);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Validate date string
|
|
106
|
+
*/
|
|
107
|
+
function isValidDate(dateString) {
|
|
108
|
+
const date = new Date(dateString);
|
|
109
|
+
return !Number.isNaN(date.getTime());
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Validate file size (in bytes)
|
|
113
|
+
*/
|
|
114
|
+
function isValidFileSize(size, maxSize) {
|
|
115
|
+
return size > 0 && size <= maxSize;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate file type
|
|
119
|
+
*/
|
|
120
|
+
function isValidFileType(filename, allowedTypes) {
|
|
121
|
+
const extension = filename.split('.').pop()?.toLowerCase();
|
|
122
|
+
return extension ? allowedTypes.includes(extension) : false;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Sanitize string input
|
|
126
|
+
*/
|
|
127
|
+
function sanitizeString(input) {
|
|
128
|
+
// Remove HTML tags entirely and trim whitespace
|
|
129
|
+
return input.trim().replace(/<[^>]*>/g, '');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Sanitize HTML input
|
|
133
|
+
*/
|
|
134
|
+
function sanitizeHTML(input) {
|
|
135
|
+
return input
|
|
136
|
+
.replace(/&/g, '&')
|
|
137
|
+
.replace(/</g, '<')
|
|
138
|
+
.replace(/>/g, '>')
|
|
139
|
+
.replace(/"/g, '"')
|
|
140
|
+
.replace(/'/g, ''');
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Validate MongoDB ObjectId format
|
|
144
|
+
* Note: This is a basic format check. For full validation, use mongoose.Types.ObjectId.isValid()
|
|
145
|
+
* This function works in environments where mongoose may not be available (e.g., client-side)
|
|
146
|
+
*/
|
|
147
|
+
function isValidObjectId(id) {
|
|
148
|
+
if (typeof id !== 'string') {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
// MongoDB ObjectId is 24 hex characters
|
|
152
|
+
const OBJECT_ID_REGEX = /^[0-9a-fA-F]{24}$/;
|
|
153
|
+
return OBJECT_ID_REGEX.test(id);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validate and sanitize user input
|
|
157
|
+
*/
|
|
158
|
+
function validateAndSanitizeUserInput(input, type) {
|
|
159
|
+
if (typeof input !== 'string') {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const sanitized = sanitizeString(input);
|
|
163
|
+
switch (type) {
|
|
164
|
+
case 'email':
|
|
165
|
+
return isValidEmail(sanitized) ? sanitized : null;
|
|
166
|
+
case 'username':
|
|
167
|
+
return isValidUsername(sanitized) ? sanitized : null;
|
|
168
|
+
case 'string':
|
|
169
|
+
return isRequiredString(sanitized) ? sanitized : null;
|
|
170
|
+
default:
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AuthManager - Centralized Authentication Manager
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified authentication interface for all platforms.
|
|
5
|
+
* Handles token storage, session management, and auth state changes.
|
|
6
|
+
*
|
|
7
|
+
* @module core/AuthManager
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Storage keys used by AuthManager.
|
|
11
|
+
*/
|
|
12
|
+
const STORAGE_KEYS = {
|
|
13
|
+
ACCESS_TOKEN: 'oxy_access_token',
|
|
14
|
+
REFRESH_TOKEN: 'oxy_refresh_token',
|
|
15
|
+
SESSION: 'oxy_session',
|
|
16
|
+
USER: 'oxy_user',
|
|
17
|
+
AUTH_METHOD: 'oxy_auth_method',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Default in-memory storage for non-browser environments.
|
|
21
|
+
*/
|
|
22
|
+
class MemoryStorage {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.store = new Map();
|
|
25
|
+
}
|
|
26
|
+
getItem(key) {
|
|
27
|
+
return this.store.get(key) ?? null;
|
|
28
|
+
}
|
|
29
|
+
setItem(key, value) {
|
|
30
|
+
this.store.set(key, value);
|
|
31
|
+
}
|
|
32
|
+
removeItem(key) {
|
|
33
|
+
this.store.delete(key);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Browser localStorage adapter.
|
|
38
|
+
*/
|
|
39
|
+
class LocalStorageAdapter {
|
|
40
|
+
getItem(key) {
|
|
41
|
+
if (typeof window === 'undefined')
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
return localStorage.getItem(key);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
setItem(key, value) {
|
|
51
|
+
if (typeof window === 'undefined')
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
localStorage.setItem(key, value);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Storage full or blocked
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
removeItem(key) {
|
|
61
|
+
if (typeof window === 'undefined')
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
localStorage.removeItem(key);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Ignore errors
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* AuthManager - Centralized authentication management.
|
|
73
|
+
*
|
|
74
|
+
* Provides a single point of control for:
|
|
75
|
+
* - Token storage and retrieval
|
|
76
|
+
* - Session management
|
|
77
|
+
* - Auth state change notifications
|
|
78
|
+
* - Multiple auth method support
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const authManager = new AuthManager(oxyServices);
|
|
83
|
+
*
|
|
84
|
+
* // Listen for auth changes
|
|
85
|
+
* authManager.onAuthStateChange((user) => {
|
|
86
|
+
* console.log('Auth state changed:', user);
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* // Handle successful auth
|
|
90
|
+
* await authManager.handleAuthSuccess(session);
|
|
91
|
+
*
|
|
92
|
+
* // Sign out
|
|
93
|
+
* await authManager.signOut();
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export class AuthManager {
|
|
97
|
+
constructor(oxyServices, config = {}) {
|
|
98
|
+
this.listeners = new Set();
|
|
99
|
+
this.currentUser = null;
|
|
100
|
+
this.refreshTimer = null;
|
|
101
|
+
this.oxyServices = oxyServices;
|
|
102
|
+
this.config = {
|
|
103
|
+
storage: config.storage ?? this.getDefaultStorage(),
|
|
104
|
+
autoRefresh: config.autoRefresh ?? true,
|
|
105
|
+
refreshBuffer: config.refreshBuffer ?? 5 * 60 * 1000, // 5 minutes
|
|
106
|
+
};
|
|
107
|
+
this.storage = this.config.storage;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get default storage based on environment.
|
|
111
|
+
*/
|
|
112
|
+
getDefaultStorage() {
|
|
113
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
114
|
+
return new LocalStorageAdapter();
|
|
115
|
+
}
|
|
116
|
+
return new MemoryStorage();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Subscribe to auth state changes.
|
|
120
|
+
*
|
|
121
|
+
* @param callback - Function called when auth state changes
|
|
122
|
+
* @returns Unsubscribe function
|
|
123
|
+
*/
|
|
124
|
+
onAuthStateChange(callback) {
|
|
125
|
+
this.listeners.add(callback);
|
|
126
|
+
// Call immediately with current state
|
|
127
|
+
callback(this.currentUser);
|
|
128
|
+
return () => {
|
|
129
|
+
this.listeners.delete(callback);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Notify all listeners of auth state change.
|
|
134
|
+
*/
|
|
135
|
+
notifyListeners() {
|
|
136
|
+
for (const listener of this.listeners) {
|
|
137
|
+
try {
|
|
138
|
+
listener(this.currentUser);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Ignore listener errors
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Handle successful authentication.
|
|
147
|
+
*
|
|
148
|
+
* @param session - Session response from auth
|
|
149
|
+
* @param method - Auth method used
|
|
150
|
+
*/
|
|
151
|
+
async handleAuthSuccess(session, method = 'credentials') {
|
|
152
|
+
// Store tokens
|
|
153
|
+
if (session.accessToken) {
|
|
154
|
+
await this.storage.setItem(STORAGE_KEYS.ACCESS_TOKEN, session.accessToken);
|
|
155
|
+
this.oxyServices.httpService.setTokens(session.accessToken);
|
|
156
|
+
}
|
|
157
|
+
// Store refresh token if available
|
|
158
|
+
if (session.refreshToken) {
|
|
159
|
+
await this.storage.setItem(STORAGE_KEYS.REFRESH_TOKEN, session.refreshToken);
|
|
160
|
+
}
|
|
161
|
+
// Store session info
|
|
162
|
+
await this.storage.setItem(STORAGE_KEYS.SESSION, JSON.stringify({
|
|
163
|
+
sessionId: session.sessionId,
|
|
164
|
+
deviceId: session.deviceId,
|
|
165
|
+
expiresAt: session.expiresAt,
|
|
166
|
+
}));
|
|
167
|
+
// Store user only if it has valid required fields (not an empty placeholder)
|
|
168
|
+
if (session.user && typeof session.user.id === 'string' && session.user.id.length > 0) {
|
|
169
|
+
await this.storage.setItem(STORAGE_KEYS.USER, JSON.stringify(session.user));
|
|
170
|
+
this.currentUser = session.user;
|
|
171
|
+
}
|
|
172
|
+
// Store auth method
|
|
173
|
+
await this.storage.setItem(STORAGE_KEYS.AUTH_METHOD, method);
|
|
174
|
+
// Setup auto-refresh if enabled
|
|
175
|
+
if (this.config.autoRefresh && session.expiresAt) {
|
|
176
|
+
this.setupTokenRefresh(session.expiresAt);
|
|
177
|
+
}
|
|
178
|
+
// Notify listeners
|
|
179
|
+
this.notifyListeners();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Setup automatic token refresh.
|
|
183
|
+
*/
|
|
184
|
+
setupTokenRefresh(expiresAt) {
|
|
185
|
+
if (this.refreshTimer) {
|
|
186
|
+
clearTimeout(this.refreshTimer);
|
|
187
|
+
}
|
|
188
|
+
const expiresAtMs = new Date(expiresAt).getTime();
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
const refreshAt = expiresAtMs - this.config.refreshBuffer;
|
|
191
|
+
const delay = Math.max(0, refreshAt - now);
|
|
192
|
+
if (delay > 0) {
|
|
193
|
+
this.refreshTimer = setTimeout(() => {
|
|
194
|
+
this.refreshToken().catch(() => {
|
|
195
|
+
// Refresh failed, user will need to re-auth
|
|
196
|
+
});
|
|
197
|
+
}, delay);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Refresh the access token.
|
|
202
|
+
*/
|
|
203
|
+
async refreshToken() {
|
|
204
|
+
const refreshToken = await this.storage.getItem(STORAGE_KEYS.REFRESH_TOKEN);
|
|
205
|
+
if (!refreshToken) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
// Cast httpService to proper type (needed due to mixin composition)
|
|
210
|
+
const httpService = this.oxyServices.httpService;
|
|
211
|
+
const response = await httpService.request({
|
|
212
|
+
method: 'POST',
|
|
213
|
+
url: '/api/auth/refresh',
|
|
214
|
+
data: { refreshToken },
|
|
215
|
+
cache: false,
|
|
216
|
+
});
|
|
217
|
+
await this.handleAuthSuccess(response, 'credentials');
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// Refresh failed, clear session and update state
|
|
222
|
+
await this.clearSession();
|
|
223
|
+
this.currentUser = null;
|
|
224
|
+
this.notifyListeners();
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Sign out and clear all auth data.
|
|
230
|
+
*/
|
|
231
|
+
async signOut() {
|
|
232
|
+
// Clear refresh timer
|
|
233
|
+
if (this.refreshTimer) {
|
|
234
|
+
clearTimeout(this.refreshTimer);
|
|
235
|
+
this.refreshTimer = null;
|
|
236
|
+
}
|
|
237
|
+
// Revoke FedCM credential if supported
|
|
238
|
+
try {
|
|
239
|
+
const services = this.oxyServices;
|
|
240
|
+
if (services.revokeFedCMCredential) {
|
|
241
|
+
await services.revokeFedCMCredential();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Ignore FedCM errors
|
|
246
|
+
}
|
|
247
|
+
// Clear HTTP client tokens
|
|
248
|
+
this.oxyServices.httpService.setTokens('');
|
|
249
|
+
// Clear storage
|
|
250
|
+
await this.clearSession();
|
|
251
|
+
// Update state and notify
|
|
252
|
+
this.currentUser = null;
|
|
253
|
+
this.notifyListeners();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Clear session data from storage.
|
|
257
|
+
*/
|
|
258
|
+
async clearSession() {
|
|
259
|
+
await this.storage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
|
|
260
|
+
await this.storage.removeItem(STORAGE_KEYS.REFRESH_TOKEN);
|
|
261
|
+
await this.storage.removeItem(STORAGE_KEYS.SESSION);
|
|
262
|
+
await this.storage.removeItem(STORAGE_KEYS.USER);
|
|
263
|
+
await this.storage.removeItem(STORAGE_KEYS.AUTH_METHOD);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get current user.
|
|
267
|
+
*/
|
|
268
|
+
getCurrentUser() {
|
|
269
|
+
return this.currentUser;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Check if user is authenticated.
|
|
273
|
+
*/
|
|
274
|
+
isAuthenticated() {
|
|
275
|
+
return this.currentUser !== null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get stored access token.
|
|
279
|
+
*/
|
|
280
|
+
async getAccessToken() {
|
|
281
|
+
return this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get the auth method used for current session.
|
|
285
|
+
*/
|
|
286
|
+
async getAuthMethod() {
|
|
287
|
+
const method = await this.storage.getItem(STORAGE_KEYS.AUTH_METHOD);
|
|
288
|
+
return method;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Initialize auth state from storage.
|
|
292
|
+
*
|
|
293
|
+
* Call this on app startup to restore previous session.
|
|
294
|
+
*/
|
|
295
|
+
async initialize() {
|
|
296
|
+
try {
|
|
297
|
+
// Try to restore user from storage
|
|
298
|
+
const userJson = await this.storage.getItem(STORAGE_KEYS.USER);
|
|
299
|
+
if (userJson) {
|
|
300
|
+
this.currentUser = JSON.parse(userJson);
|
|
301
|
+
}
|
|
302
|
+
// Restore token to HTTP client
|
|
303
|
+
const token = await this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
|
|
304
|
+
if (token) {
|
|
305
|
+
this.oxyServices.httpService.setTokens(token);
|
|
306
|
+
}
|
|
307
|
+
// Check session expiry
|
|
308
|
+
const sessionJson = await this.storage.getItem(STORAGE_KEYS.SESSION);
|
|
309
|
+
if (sessionJson) {
|
|
310
|
+
const session = JSON.parse(sessionJson);
|
|
311
|
+
if (session.expiresAt) {
|
|
312
|
+
const expiresAt = new Date(session.expiresAt).getTime();
|
|
313
|
+
if (expiresAt <= Date.now()) {
|
|
314
|
+
// Session expired, try refresh
|
|
315
|
+
const refreshed = await this.refreshToken();
|
|
316
|
+
if (!refreshed) {
|
|
317
|
+
await this.clearSession();
|
|
318
|
+
this.currentUser = null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (this.config.autoRefresh) {
|
|
322
|
+
// Setup refresh timer
|
|
323
|
+
this.setupTokenRefresh(session.expiresAt);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return this.currentUser;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// Failed to restore, start fresh
|
|
331
|
+
await this.clearSession();
|
|
332
|
+
this.currentUser = null;
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Destroy the auth manager and clean up resources.
|
|
338
|
+
*/
|
|
339
|
+
destroy() {
|
|
340
|
+
if (this.refreshTimer) {
|
|
341
|
+
clearTimeout(this.refreshTimer);
|
|
342
|
+
this.refreshTimer = null;
|
|
343
|
+
}
|
|
344
|
+
this.listeners.clear();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Create an AuthManager instance.
|
|
349
|
+
*
|
|
350
|
+
* @param oxyServices - OxyServices instance
|
|
351
|
+
* @param config - Optional configuration
|
|
352
|
+
* @returns AuthManager instance
|
|
353
|
+
*/
|
|
354
|
+
export function createAuthManager(oxyServices, config) {
|
|
355
|
+
return new AuthManager(oxyServices, config);
|
|
356
|
+
}
|