@oxyhq/core 1.6.0 → 1.6.2
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/dist/cjs/AuthManager.js +15 -0
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/mixins/OxyServices.fedcm.js +39 -3
- package/dist/cjs/mixins/OxyServices.utility.js +24 -4
- package/dist/cjs/utils/authHelpers.js +114 -0
- package/dist/esm/AuthManager.js +16 -1
- package/dist/esm/HttpService.js +6 -6
- package/dist/esm/OxyServices.base.js +3 -3
- package/dist/esm/OxyServices.js +2 -2
- package/dist/esm/crypto/index.js +5 -5
- package/dist/esm/crypto/keyManager.js +3 -3
- package/dist/esm/crypto/recoveryPhrase.js +1 -1
- package/dist/esm/crypto/signatureService.js +2 -2
- package/dist/esm/i18n/index.js +11 -11
- package/dist/esm/index.js +27 -25
- package/dist/esm/mixins/OxyServices.analytics.js +1 -1
- package/dist/esm/mixins/OxyServices.auth.js +1 -1
- package/dist/esm/mixins/OxyServices.developer.js +1 -1
- package/dist/esm/mixins/OxyServices.features.js +1 -1
- package/dist/esm/mixins/OxyServices.fedcm.js +41 -5
- package/dist/esm/mixins/OxyServices.karma.js +1 -1
- package/dist/esm/mixins/OxyServices.language.js +2 -2
- package/dist/esm/mixins/OxyServices.payment.js +1 -1
- package/dist/esm/mixins/OxyServices.popup.js +2 -2
- package/dist/esm/mixins/OxyServices.privacy.js +1 -1
- package/dist/esm/mixins/OxyServices.redirect.js +1 -1
- package/dist/esm/mixins/OxyServices.security.js +1 -1
- package/dist/esm/mixins/OxyServices.user.js +1 -1
- package/dist/esm/mixins/OxyServices.utility.js +25 -5
- package/dist/esm/mixins/index.js +18 -18
- package/dist/esm/shared/index.js +5 -5
- package/dist/esm/shared/utils/index.js +4 -4
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/authHelpers.js +105 -0
- package/dist/esm/utils/errorUtils.js +1 -1
- package/dist/esm/utils/index.js +4 -4
- package/dist/types/index.d.ts +2 -0
- package/dist/types/mixins/OxyServices.fedcm.d.ts +6 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +13 -0
- package/dist/types/utils/authHelpers.d.ts +57 -0
- package/package.json +2 -2
- package/src/AuthManager.ts +15 -0
- package/src/index.ts +11 -0
- package/src/mixins/OxyServices.fedcm.ts +44 -3
- package/src/mixins/OxyServices.utility.ts +24 -4
- package/src/utils/authHelpers.ts +140 -0
package/dist/cjs/AuthManager.js
CHANGED
|
@@ -20,6 +20,7 @@ const STORAGE_KEYS = {
|
|
|
20
20
|
SESSION: 'oxy_session',
|
|
21
21
|
USER: 'oxy_user',
|
|
22
22
|
AUTH_METHOD: 'oxy_auth_method',
|
|
23
|
+
FEDCM_LOGIN_HINT: 'oxy_fedcm_login_hint',
|
|
23
24
|
};
|
|
24
25
|
/**
|
|
25
26
|
* Default in-memory storage for non-browser environments.
|
|
@@ -300,6 +301,19 @@ class AuthManager {
|
|
|
300
301
|
clearTimeout(this.refreshTimer);
|
|
301
302
|
this.refreshTimer = null;
|
|
302
303
|
}
|
|
304
|
+
// Invalidate current session on the server (best-effort)
|
|
305
|
+
try {
|
|
306
|
+
const sessionJson = await this.storage.getItem(STORAGE_KEYS.SESSION);
|
|
307
|
+
if (sessionJson) {
|
|
308
|
+
const session = JSON.parse(sessionJson);
|
|
309
|
+
if (session.sessionId && typeof this.oxyServices.logoutSession === 'function') {
|
|
310
|
+
await this.oxyServices.logoutSession(session.sessionId);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Best-effort: don't block local signout on network failure
|
|
316
|
+
}
|
|
303
317
|
// Revoke FedCM credential if supported
|
|
304
318
|
try {
|
|
305
319
|
const services = this.oxyServices;
|
|
@@ -327,6 +341,7 @@ class AuthManager {
|
|
|
327
341
|
await this.storage.removeItem(STORAGE_KEYS.SESSION);
|
|
328
342
|
await this.storage.removeItem(STORAGE_KEYS.USER);
|
|
329
343
|
await this.storage.removeItem(STORAGE_KEYS.AUTH_METHOD);
|
|
344
|
+
await this.storage.removeItem(STORAGE_KEYS.FEDCM_LOGIN_HINT);
|
|
330
345
|
}
|
|
331
346
|
/**
|
|
332
347
|
* Get current user.
|
package/dist/cjs/index.js
CHANGED
|
@@ -30,7 +30,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
32
|
exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.normalizeLanguageCode = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.SUPPORTED_LANGUAGES = exports.DeviceManager = exports.RecoveryPhraseService = exports.SignatureService = exports.KeyManager = exports.createCrossDomainAuth = exports.CrossDomainAuth = exports.createAuthManager = exports.AuthManager = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyServices = void 0;
|
|
33
|
-
exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.packageInfo = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = void 0;
|
|
33
|
+
exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.packageInfo = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = void 0;
|
|
34
34
|
// Ensure crypto polyfills are loaded before anything else
|
|
35
35
|
require("./crypto/polyfill");
|
|
36
36
|
// --- Core API Client ---
|
|
@@ -119,6 +119,14 @@ Object.defineProperty(exports, "createDebugLogger", { enumerable: true, get: fun
|
|
|
119
119
|
// --- i18n ---
|
|
120
120
|
var i18n_1 = require("./i18n");
|
|
121
121
|
Object.defineProperty(exports, "translate", { enumerable: true, get: function () { return i18n_1.translate; } });
|
|
122
|
+
// --- Auth Helpers ---
|
|
123
|
+
var authHelpers_1 = require("./utils/authHelpers");
|
|
124
|
+
Object.defineProperty(exports, "SessionSyncRequiredError", { enumerable: true, get: function () { return authHelpers_1.SessionSyncRequiredError; } });
|
|
125
|
+
Object.defineProperty(exports, "AuthenticationFailedError", { enumerable: true, get: function () { return authHelpers_1.AuthenticationFailedError; } });
|
|
126
|
+
Object.defineProperty(exports, "ensureValidToken", { enumerable: true, get: function () { return authHelpers_1.ensureValidToken; } });
|
|
127
|
+
Object.defineProperty(exports, "isAuthenticationError", { enumerable: true, get: function () { return authHelpers_1.isAuthenticationError; } });
|
|
128
|
+
Object.defineProperty(exports, "withAuthErrorHandling", { enumerable: true, get: function () { return authHelpers_1.withAuthErrorHandling; } });
|
|
129
|
+
Object.defineProperty(exports, "authenticatedApiCall", { enumerable: true, get: function () { return authHelpers_1.authenticatedApiCall; } });
|
|
122
130
|
// --- Session Utilities ---
|
|
123
131
|
var sessionUtils_1 = require("./utils/sessionUtils");
|
|
124
132
|
Object.defineProperty(exports, "mergeSessions", { enumerable: true, get: function () { return sessionUtils_1.mergeSessions; } });
|
|
@@ -5,6 +5,7 @@ exports.FedCMMixin = OxyServicesFedCMMixin;
|
|
|
5
5
|
const OxyServices_errors_1 = require("../OxyServices.errors");
|
|
6
6
|
const debugUtils_1 = require("../shared/utils/debugUtils");
|
|
7
7
|
const debug = (0, debugUtils_1.createDebugLogger)('FedCM');
|
|
8
|
+
const FEDCM_LOGIN_HINT_KEY = 'oxy_fedcm_login_hint';
|
|
8
9
|
// Global lock to prevent concurrent FedCM requests
|
|
9
10
|
// FedCM only allows one navigator.credentials.get request at a time
|
|
10
11
|
let fedCMRequestInProgress = false;
|
|
@@ -82,13 +83,16 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
82
83
|
try {
|
|
83
84
|
const nonce = options.nonce || this.generateNonce();
|
|
84
85
|
const clientId = this.getClientId();
|
|
85
|
-
|
|
86
|
+
// Use provided loginHint, or fall back to stored last-used account ID
|
|
87
|
+
const loginHint = options.loginHint || this.getStoredLoginHint();
|
|
88
|
+
debug.log('Interactive sign-in: Requesting credential for', clientId, loginHint ? `(hint: ${loginHint})` : '');
|
|
86
89
|
// Request credential from browser's native identity flow
|
|
87
90
|
const credential = await this.requestIdentityCredential({
|
|
88
91
|
configURL: this.constructor.DEFAULT_CONFIG_URL,
|
|
89
92
|
clientId,
|
|
90
93
|
nonce,
|
|
91
94
|
context: options.context,
|
|
95
|
+
loginHint,
|
|
92
96
|
});
|
|
93
97
|
if (!credential || !credential.token) {
|
|
94
98
|
throw new OxyServices_errors_1.OxyAuthenticationError('No credential received from browser');
|
|
@@ -100,6 +104,10 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
100
104
|
if (session && session.accessToken) {
|
|
101
105
|
this.httpService.setTokens(session.accessToken);
|
|
102
106
|
}
|
|
107
|
+
// Store the user ID as loginHint for future FedCM requests
|
|
108
|
+
if (session?.user?.id) {
|
|
109
|
+
this.storeLoginHint(session.user.id);
|
|
110
|
+
}
|
|
103
111
|
debug.log('Interactive sign-in: Success!', { userId: session?.user?.id });
|
|
104
112
|
return session;
|
|
105
113
|
}
|
|
@@ -164,13 +172,15 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
164
172
|
// this runs on app startup — showing browser UI without user action is bad UX.
|
|
165
173
|
// Optional/interactive mediation should only happen when the user clicks "Sign In".
|
|
166
174
|
let credential = null;
|
|
175
|
+
const loginHint = this.getStoredLoginHint();
|
|
167
176
|
try {
|
|
168
177
|
const nonce = this.generateNonce();
|
|
169
|
-
debug.log('Silent SSO: Attempting silent mediation...');
|
|
178
|
+
debug.log('Silent SSO: Attempting silent mediation...', loginHint ? `(hint: ${loginHint})` : '');
|
|
170
179
|
credential = await this.requestIdentityCredential({
|
|
171
180
|
configURL: this.constructor.DEFAULT_CONFIG_URL,
|
|
172
181
|
clientId,
|
|
173
182
|
nonce,
|
|
183
|
+
loginHint,
|
|
174
184
|
mediation: 'silent',
|
|
175
185
|
});
|
|
176
186
|
debug.log('Silent SSO: Silent mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
|
|
@@ -225,6 +235,10 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
225
235
|
else {
|
|
226
236
|
debug.warn('Silent SSO: No accessToken in session response');
|
|
227
237
|
}
|
|
238
|
+
// Store the user ID as loginHint for future FedCM requests
|
|
239
|
+
if (session.user?.id) {
|
|
240
|
+
this.storeLoginHint(session.user.id);
|
|
241
|
+
}
|
|
228
242
|
debug.log('Silent SSO: Success!', {
|
|
229
243
|
sessionId: session.sessionId?.substring(0, 8) + '...',
|
|
230
244
|
userId: session.user?.id
|
|
@@ -299,7 +313,7 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
299
313
|
params: {
|
|
300
314
|
nonce: options.nonce, // For Chrome 145+
|
|
301
315
|
},
|
|
302
|
-
...(options.
|
|
316
|
+
...(options.loginHint && { loginHint: options.loginHint }),
|
|
303
317
|
},
|
|
304
318
|
],
|
|
305
319
|
},
|
|
@@ -419,6 +433,28 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
419
433
|
}
|
|
420
434
|
return window.location.origin;
|
|
421
435
|
}
|
|
436
|
+
/** @internal */
|
|
437
|
+
getStoredLoginHint() {
|
|
438
|
+
if (typeof window === 'undefined')
|
|
439
|
+
return undefined;
|
|
440
|
+
try {
|
|
441
|
+
return localStorage.getItem(FEDCM_LOGIN_HINT_KEY) || undefined;
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/** @internal */
|
|
448
|
+
storeLoginHint(userId) {
|
|
449
|
+
if (typeof window === 'undefined')
|
|
450
|
+
return;
|
|
451
|
+
try {
|
|
452
|
+
localStorage.setItem(FEDCM_LOGIN_HINT_KEY, userId);
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
// Storage full or blocked
|
|
456
|
+
}
|
|
457
|
+
}
|
|
422
458
|
},
|
|
423
459
|
_a.DEFAULT_CONFIG_URL = 'https://auth.oxy.so/fedcm.json',
|
|
424
460
|
_a.FEDCM_TIMEOUT = 15000 // 15 seconds for interactive
|
|
@@ -67,6 +67,17 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
67
67
|
* Validates JWT tokens against the Oxy API and attaches user data to requests.
|
|
68
68
|
* Uses server-side session validation for security (not just JWT decode).
|
|
69
69
|
*
|
|
70
|
+
* **Design note — jwtDecode vs jwt.verify:**
|
|
71
|
+
* This middleware intentionally uses `jwtDecode()` (decode-only, no signature
|
|
72
|
+
* verification) for user tokens. This is by design, NOT a security gap:
|
|
73
|
+
* - Third-party apps using `oxy.auth()` don't have the Oxy JWT secret
|
|
74
|
+
* - Security comes from API-based session validation (`validateSession()`)
|
|
75
|
+
* which checks the session server-side on every request
|
|
76
|
+
* - Service tokens (type: 'service') DO use cryptographic HMAC verification
|
|
77
|
+
* via the `jwtSecret` option, since they are stateless
|
|
78
|
+
* - The backend's own `authMiddleware` uses `jwt.verify()` because it has
|
|
79
|
+
* direct access to `ACCESS_TOKEN_SECRET`
|
|
80
|
+
*
|
|
70
81
|
* @example
|
|
71
82
|
* ```typescript
|
|
72
83
|
* import { OxyServices } from '@oxyhq/core';
|
|
@@ -119,6 +130,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
119
130
|
return next();
|
|
120
131
|
}
|
|
121
132
|
const error = {
|
|
133
|
+
error: 'MISSING_TOKEN',
|
|
122
134
|
message: 'Access token required',
|
|
123
135
|
code: 'MISSING_TOKEN',
|
|
124
136
|
status: 401
|
|
@@ -139,6 +151,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
139
151
|
return next();
|
|
140
152
|
}
|
|
141
153
|
const error = {
|
|
154
|
+
error: 'INVALID_TOKEN_FORMAT',
|
|
142
155
|
message: 'Invalid token format',
|
|
143
156
|
code: 'INVALID_TOKEN_FORMAT',
|
|
144
157
|
status: 401
|
|
@@ -158,6 +171,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
158
171
|
return next();
|
|
159
172
|
}
|
|
160
173
|
const error = {
|
|
174
|
+
error: 'SERVICE_TOKEN_NOT_CONFIGURED',
|
|
161
175
|
message: 'Service token verification not configured',
|
|
162
176
|
code: 'SERVICE_TOKEN_NOT_CONFIGURED',
|
|
163
177
|
status: 403
|
|
@@ -192,7 +206,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
192
206
|
(verifyError.message === 'Invalid signature' || verifyError.message === 'Invalid token structure');
|
|
193
207
|
if (!isSignatureError) {
|
|
194
208
|
console.error('[oxy.auth] Unexpected error during service token verification:', verifyError);
|
|
195
|
-
const error = { message: 'Internal authentication error', code: 'AUTH_INTERNAL_ERROR', status: 500 };
|
|
209
|
+
const error = { error: 'AUTH_INTERNAL_ERROR', message: 'Internal authentication error', code: 'AUTH_INTERNAL_ERROR', status: 500 };
|
|
196
210
|
if (onError)
|
|
197
211
|
return onError(error);
|
|
198
212
|
return res.status(500).json(error);
|
|
@@ -202,7 +216,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
202
216
|
req.user = null;
|
|
203
217
|
return next();
|
|
204
218
|
}
|
|
205
|
-
const error = { message: 'Invalid service token signature', code: 'INVALID_SERVICE_TOKEN', status: 401 };
|
|
219
|
+
const error = { error: 'INVALID_SERVICE_TOKEN', message: 'Invalid service token signature', code: 'INVALID_SERVICE_TOKEN', status: 401 };
|
|
206
220
|
if (onError)
|
|
207
221
|
return onError(error);
|
|
208
222
|
return res.status(401).json(error);
|
|
@@ -214,7 +228,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
214
228
|
req.user = null;
|
|
215
229
|
return next();
|
|
216
230
|
}
|
|
217
|
-
const error = { message: 'Service token expired', code: 'TOKEN_EXPIRED', status: 401 };
|
|
231
|
+
const error = { error: 'TOKEN_EXPIRED', message: 'Service token expired', code: 'TOKEN_EXPIRED', status: 401 };
|
|
218
232
|
if (onError)
|
|
219
233
|
return onError(error);
|
|
220
234
|
return res.status(401).json(error);
|
|
@@ -226,7 +240,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
226
240
|
req.user = null;
|
|
227
241
|
return next();
|
|
228
242
|
}
|
|
229
|
-
const error = { message: 'Invalid service token: missing appId', code: 'INVALID_SERVICE_TOKEN', status: 401 };
|
|
243
|
+
const error = { error: 'INVALID_SERVICE_TOKEN', message: 'Invalid service token: missing appId', code: 'INVALID_SERVICE_TOKEN', status: 401 };
|
|
230
244
|
if (onError)
|
|
231
245
|
return onError(error);
|
|
232
246
|
return res.status(401).json(error);
|
|
@@ -253,6 +267,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
253
267
|
return next();
|
|
254
268
|
}
|
|
255
269
|
const error = {
|
|
270
|
+
error: 'INVALID_TOKEN_PAYLOAD',
|
|
256
271
|
message: 'Token missing user ID',
|
|
257
272
|
code: 'INVALID_TOKEN_PAYLOAD',
|
|
258
273
|
status: 401
|
|
@@ -269,6 +284,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
269
284
|
return next();
|
|
270
285
|
}
|
|
271
286
|
const error = {
|
|
287
|
+
error: 'TOKEN_EXPIRED',
|
|
272
288
|
message: 'Token expired',
|
|
273
289
|
code: 'TOKEN_EXPIRED',
|
|
274
290
|
status: 401
|
|
@@ -291,6 +307,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
291
307
|
return next();
|
|
292
308
|
}
|
|
293
309
|
const error = {
|
|
310
|
+
error: 'INVALID_SESSION',
|
|
294
311
|
message: 'Session invalid or expired',
|
|
295
312
|
code: 'INVALID_SESSION',
|
|
296
313
|
status: 401
|
|
@@ -325,6 +342,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
325
342
|
return next();
|
|
326
343
|
}
|
|
327
344
|
const error = {
|
|
345
|
+
error: 'SESSION_VALIDATION_ERROR',
|
|
328
346
|
message: 'Session validation failed',
|
|
329
347
|
code: 'SESSION_VALIDATION_ERROR',
|
|
330
348
|
status: 401
|
|
@@ -382,6 +400,8 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
382
400
|
* Returns a middleware function for Socket.IO that validates JWT tokens
|
|
383
401
|
* from the handshake auth object and attaches user data to the socket.
|
|
384
402
|
*
|
|
403
|
+
* Uses `jwtDecode()` + API session validation (same rationale as `auth()`).
|
|
404
|
+
*
|
|
385
405
|
* @example
|
|
386
406
|
* ```typescript
|
|
387
407
|
* import { OxyServices } from '@oxyhq/core';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication helper utilities for common token validation
|
|
4
|
+
* and authentication error handling patterns.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AuthenticationFailedError = exports.SessionSyncRequiredError = void 0;
|
|
8
|
+
exports.ensureValidToken = ensureValidToken;
|
|
9
|
+
exports.isAuthenticationError = isAuthenticationError;
|
|
10
|
+
exports.withAuthErrorHandling = withAuthErrorHandling;
|
|
11
|
+
exports.authenticatedApiCall = authenticatedApiCall;
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when session sync is required
|
|
14
|
+
*/
|
|
15
|
+
class SessionSyncRequiredError extends Error {
|
|
16
|
+
constructor(message = 'Session needs to be synced. Please try again.') {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'SessionSyncRequiredError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.SessionSyncRequiredError = SessionSyncRequiredError;
|
|
22
|
+
/**
|
|
23
|
+
* Error thrown when authentication fails
|
|
24
|
+
*/
|
|
25
|
+
class AuthenticationFailedError extends Error {
|
|
26
|
+
constructor(message = 'Authentication failed. Please sign in again.') {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'AuthenticationFailedError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.AuthenticationFailedError = AuthenticationFailedError;
|
|
32
|
+
/**
|
|
33
|
+
* Ensures a valid token exists before making authenticated API calls.
|
|
34
|
+
* If no valid token exists and an active session ID is available,
|
|
35
|
+
* attempts to refresh the token using the session.
|
|
36
|
+
*
|
|
37
|
+
* @throws {SessionSyncRequiredError} If the session needs to be synced (offline session)
|
|
38
|
+
*/
|
|
39
|
+
async function ensureValidToken(oxyServices, activeSessionId) {
|
|
40
|
+
if (oxyServices.hasValidToken() || !activeSessionId) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await oxyServices.getTokenBySession(activeSessionId);
|
|
45
|
+
}
|
|
46
|
+
catch (tokenError) {
|
|
47
|
+
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
48
|
+
if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
|
|
49
|
+
throw new SessionSyncRequiredError();
|
|
50
|
+
}
|
|
51
|
+
throw tokenError;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Checks if an error is an authentication error (401 or auth-related message)
|
|
56
|
+
*/
|
|
57
|
+
function isAuthenticationError(error) {
|
|
58
|
+
if (!error || typeof error !== 'object') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const errorObj = error;
|
|
62
|
+
const errorMessage = errorObj.message || '';
|
|
63
|
+
const status = errorObj.status || errorObj.response?.status;
|
|
64
|
+
return (status === 401 ||
|
|
65
|
+
errorMessage.includes('Authentication required') ||
|
|
66
|
+
errorMessage.includes('Invalid or missing authorization header'));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Wraps an API call with authentication error handling.
|
|
70
|
+
* On auth failure, optionally attempts to sync the session and retry.
|
|
71
|
+
*
|
|
72
|
+
* @throws {AuthenticationFailedError} If authentication fails and cannot be recovered
|
|
73
|
+
*/
|
|
74
|
+
async function withAuthErrorHandling(apiCall, options) {
|
|
75
|
+
try {
|
|
76
|
+
return await apiCall();
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (!isAuthenticationError(error)) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (options?.syncSession && options?.activeSessionId && options?.oxyServices) {
|
|
83
|
+
try {
|
|
84
|
+
await options.syncSession();
|
|
85
|
+
await options.oxyServices.getTokenBySession(options.activeSessionId);
|
|
86
|
+
return await apiCall();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new AuthenticationFailedError();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new AuthenticationFailedError();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Combines token validation and auth error handling for a complete authenticated API call.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* return await authenticatedApiCall(
|
|
101
|
+
* oxyServices,
|
|
102
|
+
* activeSessionId,
|
|
103
|
+
* () => oxyServices.updateProfile(updates)
|
|
104
|
+
* );
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
async function authenticatedApiCall(oxyServices, activeSessionId, apiCall, syncSession) {
|
|
108
|
+
await ensureValidToken(oxyServices, activeSessionId);
|
|
109
|
+
return withAuthErrorHandling(apiCall, {
|
|
110
|
+
syncSession,
|
|
111
|
+
activeSessionId,
|
|
112
|
+
oxyServices,
|
|
113
|
+
});
|
|
114
|
+
}
|
package/dist/esm/AuthManager.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module core/AuthManager
|
|
8
8
|
*/
|
|
9
|
-
import { retryAsync } from './utils/asyncUtils';
|
|
9
|
+
import { retryAsync } from './utils/asyncUtils.js';
|
|
10
10
|
/**
|
|
11
11
|
* Storage keys used by AuthManager.
|
|
12
12
|
*/
|
|
@@ -16,6 +16,7 @@ const STORAGE_KEYS = {
|
|
|
16
16
|
SESSION: 'oxy_session',
|
|
17
17
|
USER: 'oxy_user',
|
|
18
18
|
AUTH_METHOD: 'oxy_auth_method',
|
|
19
|
+
FEDCM_LOGIN_HINT: 'oxy_fedcm_login_hint',
|
|
19
20
|
};
|
|
20
21
|
/**
|
|
21
22
|
* Default in-memory storage for non-browser environments.
|
|
@@ -296,6 +297,19 @@ export class AuthManager {
|
|
|
296
297
|
clearTimeout(this.refreshTimer);
|
|
297
298
|
this.refreshTimer = null;
|
|
298
299
|
}
|
|
300
|
+
// Invalidate current session on the server (best-effort)
|
|
301
|
+
try {
|
|
302
|
+
const sessionJson = await this.storage.getItem(STORAGE_KEYS.SESSION);
|
|
303
|
+
if (sessionJson) {
|
|
304
|
+
const session = JSON.parse(sessionJson);
|
|
305
|
+
if (session.sessionId && typeof this.oxyServices.logoutSession === 'function') {
|
|
306
|
+
await this.oxyServices.logoutSession(session.sessionId);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// Best-effort: don't block local signout on network failure
|
|
312
|
+
}
|
|
299
313
|
// Revoke FedCM credential if supported
|
|
300
314
|
try {
|
|
301
315
|
const services = this.oxyServices;
|
|
@@ -323,6 +337,7 @@ export class AuthManager {
|
|
|
323
337
|
await this.storage.removeItem(STORAGE_KEYS.SESSION);
|
|
324
338
|
await this.storage.removeItem(STORAGE_KEYS.USER);
|
|
325
339
|
await this.storage.removeItem(STORAGE_KEYS.AUTH_METHOD);
|
|
340
|
+
await this.storage.removeItem(STORAGE_KEYS.FEDCM_LOGIN_HINT);
|
|
326
341
|
}
|
|
327
342
|
/**
|
|
328
343
|
* Get current user.
|
package/dist/esm/HttpService.js
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
* - Error handling
|
|
13
13
|
* - Request queuing
|
|
14
14
|
*/
|
|
15
|
-
import { TTLCache, registerCacheForCleanup } from './utils/cache';
|
|
16
|
-
import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/requestUtils';
|
|
17
|
-
import { retryAsync } from './utils/asyncUtils';
|
|
18
|
-
import { handleHttpError } from './utils/errorUtils';
|
|
19
|
-
import { isDev } from './shared/utils/debugUtils';
|
|
15
|
+
import { TTLCache, registerCacheForCleanup } from './utils/cache.js';
|
|
16
|
+
import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/requestUtils.js';
|
|
17
|
+
import { retryAsync } from './utils/asyncUtils.js';
|
|
18
|
+
import { handleHttpError } from './utils/errorUtils.js';
|
|
19
|
+
import { isDev } from './shared/utils/debugUtils.js';
|
|
20
20
|
import { jwtDecode } from 'jwt-decode';
|
|
21
|
-
import { isNative, getPlatformOS } from './utils/platform';
|
|
21
|
+
import { isNative, getPlatformOS } from './utils/platform.js';
|
|
22
22
|
/**
|
|
23
23
|
* Check if we're running in a native app environment (React Native, not web)
|
|
24
24
|
* This is used to determine CSRF handling mode
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Contains core infrastructure, HTTP client, request management, and error handling
|
|
5
5
|
*/
|
|
6
6
|
import { jwtDecode } from 'jwt-decode';
|
|
7
|
-
import { handleHttpError } from './utils/errorUtils';
|
|
8
|
-
import { HttpService } from './HttpService';
|
|
9
|
-
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
7
|
+
import { handleHttpError } from './utils/errorUtils.js';
|
|
8
|
+
import { HttpService } from './HttpService.js';
|
|
9
|
+
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors.js';
|
|
10
10
|
/**
|
|
11
11
|
* Base class for OxyServices with core infrastructure
|
|
12
12
|
*/
|
package/dist/esm/OxyServices.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
1
|
+
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors.js';
|
|
2
2
|
// Import mixin composition helper
|
|
3
|
-
import { composeOxyServices } from './mixins';
|
|
3
|
+
import { composeOxyServices } from './mixins.js';
|
|
4
4
|
/**
|
|
5
5
|
* OxyServices - Unified client library for interacting with the Oxy API
|
|
6
6
|
*
|
package/dist/esm/crypto/index.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Handles key generation, secure storage, digital signatures, and recovery phrases.
|
|
6
6
|
*/
|
|
7
7
|
// Import polyfills first - this ensures Buffer is available for bip39 and other libraries
|
|
8
|
-
import './polyfill';
|
|
9
|
-
export { KeyManager } from './keyManager';
|
|
10
|
-
export { SignatureService } from './signatureService';
|
|
11
|
-
export { RecoveryPhraseService } from './recoveryPhrase';
|
|
8
|
+
import './polyfill.js';
|
|
9
|
+
export { KeyManager } from './keyManager.js';
|
|
10
|
+
export { SignatureService } from './signatureService.js';
|
|
11
|
+
export { RecoveryPhraseService } from './recoveryPhrase.js';
|
|
12
12
|
// Re-export for convenience
|
|
13
|
-
export { KeyManager as default } from './keyManager';
|
|
13
|
+
export { KeyManager as default } from './keyManager.js';
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Private keys are stored securely using expo-secure-store and never leave the device.
|
|
6
6
|
*/
|
|
7
7
|
import { ec as EC } from 'elliptic';
|
|
8
|
-
import { isWeb, isIOS, isAndroid, isReactNative, isNodeJS } from '../utils/platform';
|
|
9
|
-
import { logger } from '../utils/loggerUtils';
|
|
10
|
-
import { isDev } from '../shared/utils/debugUtils';
|
|
8
|
+
import { isWeb, isIOS, isAndroid, isReactNative, isNodeJS } from '../utils/platform.js';
|
|
9
|
+
import { logger } from '../utils/loggerUtils.js';
|
|
10
|
+
import { isDev } from '../shared/utils/debugUtils.js';
|
|
11
11
|
// Lazy imports for React Native specific modules
|
|
12
12
|
let SecureStore = null;
|
|
13
13
|
let ExpoCrypto = null;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Note: This module requires the polyfill to be loaded first (done via crypto/index.ts)
|
|
8
8
|
*/
|
|
9
9
|
import * as bip39 from 'bip39';
|
|
10
|
-
import { KeyManager } from './keyManager';
|
|
10
|
+
import { KeyManager } from './keyManager.js';
|
|
11
11
|
/**
|
|
12
12
|
* Convert Uint8Array or array-like to hexadecimal string
|
|
13
13
|
* Works in both Node.js and React Native without depending on Buffer
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Used for authenticating requests and proving identity ownership.
|
|
6
6
|
*/
|
|
7
7
|
import { ec as EC } from 'elliptic';
|
|
8
|
-
import { KeyManager } from './keyManager';
|
|
9
|
-
import { isReactNative, isNodeJS } from '../utils/platform';
|
|
8
|
+
import { KeyManager } from './keyManager.js';
|
|
9
|
+
import { isReactNative, isNodeJS } from '../utils/platform.js';
|
|
10
10
|
// Lazy import for expo-crypto
|
|
11
11
|
let ExpoCrypto = null;
|
|
12
12
|
const ec = new EC('secp256k1');
|
package/dist/esm/i18n/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import enUS from './locales/en-US.json';
|
|
2
|
-
import esES from './locales/es-ES.json';
|
|
3
|
-
import caES from './locales/ca-ES.json';
|
|
4
|
-
import frFR from './locales/fr-FR.json';
|
|
5
|
-
import deDE from './locales/de-DE.json';
|
|
6
|
-
import itIT from './locales/it-IT.json';
|
|
7
|
-
import ptPT from './locales/pt-PT.json';
|
|
8
|
-
import jaJP from './locales/ja-JP.json';
|
|
9
|
-
import koKR from './locales/ko-KR.json';
|
|
10
|
-
import zhCN from './locales/zh-CN.json';
|
|
11
|
-
import arSA from './locales/ar-SA.json';
|
|
1
|
+
import enUS from './locales/en-US.json.js';
|
|
2
|
+
import esES from './locales/es-ES.json.js';
|
|
3
|
+
import caES from './locales/ca-ES.json.js';
|
|
4
|
+
import frFR from './locales/fr-FR.json.js';
|
|
5
|
+
import deDE from './locales/de-DE.json.js';
|
|
6
|
+
import itIT from './locales/it-IT.json.js';
|
|
7
|
+
import ptPT from './locales/pt-PT.json.js';
|
|
8
|
+
import jaJP from './locales/ja-JP.json.js';
|
|
9
|
+
import koKR from './locales/ko-KR.json.js';
|
|
10
|
+
import zhCN from './locales/zh-CN.json.js';
|
|
11
|
+
import arSA from './locales/ar-SA.json.js';
|
|
12
12
|
const DICTS = {
|
|
13
13
|
'en': enUS,
|
|
14
14
|
'en-US': enUS,
|
package/dist/esm/index.js
CHANGED
|
@@ -14,42 +14,44 @@
|
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
// Ensure crypto polyfills are loaded before anything else
|
|
17
|
-
import './crypto/polyfill';
|
|
17
|
+
import './crypto/polyfill.js';
|
|
18
18
|
// --- Core API Client ---
|
|
19
|
-
export { OxyServices, OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices';
|
|
20
|
-
export { OXY_CLOUD_URL, oxyClient } from './OxyServices';
|
|
19
|
+
export { OxyServices, OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.js';
|
|
20
|
+
export { OXY_CLOUD_URL, oxyClient } from './OxyServices.js';
|
|
21
21
|
// --- Authentication ---
|
|
22
|
-
export { AuthManager, createAuthManager } from './AuthManager';
|
|
23
|
-
export { CrossDomainAuth, createCrossDomainAuth } from './CrossDomainAuth';
|
|
22
|
+
export { AuthManager, createAuthManager } from './AuthManager.js';
|
|
23
|
+
export { CrossDomainAuth, createCrossDomainAuth } from './CrossDomainAuth.js';
|
|
24
24
|
// --- Crypto / Identity ---
|
|
25
|
-
export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
|
|
25
|
+
export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto.js';
|
|
26
26
|
// --- Models & Types ---
|
|
27
|
-
export * from './models/interfaces';
|
|
28
|
-
export * from './models/session';
|
|
27
|
+
export * from './models/interfaces.js';
|
|
28
|
+
export * from './models/session.js';
|
|
29
29
|
// --- Device Management ---
|
|
30
|
-
export { DeviceManager } from './utils/deviceManager';
|
|
30
|
+
export { DeviceManager } from './utils/deviceManager.js';
|
|
31
31
|
// --- Language Utilities ---
|
|
32
|
-
export { SUPPORTED_LANGUAGES, getLanguageMetadata, getLanguageName, getNativeLanguageName, normalizeLanguageCode, } from './utils/languageUtils';
|
|
32
|
+
export { SUPPORTED_LANGUAGES, getLanguageMetadata, getLanguageName, getNativeLanguageName, normalizeLanguageCode, } from './utils/languageUtils.js';
|
|
33
33
|
// --- Platform Detection ---
|
|
34
|
-
export { getPlatformOS, setPlatformOS, isWeb, isNative, isIOS, isAndroid, } from './utils/platform';
|
|
34
|
+
export { getPlatformOS, setPlatformOS, isWeb, isNative, isIOS, isAndroid, } from './utils/platform.js';
|
|
35
35
|
// --- Shared Utilities ---
|
|
36
|
-
export { darkenColor, lightenColor, hexToRgb, rgbToHex, withOpacity, isLightColor, getContrastTextColor, } from './shared/utils/colorUtils';
|
|
37
|
-
export { normalizeTheme, normalizeColorScheme, getOppositeTheme, systemPrefersDarkMode, getSystemColorScheme, } from './shared/utils/themeUtils';
|
|
38
|
-
export { HttpStatus, getErrorStatus, getErrorMessage, isAlreadyRegisteredError, isUnauthorizedError, isForbiddenError, isNotFoundError, isRateLimitError, isServerError, isNetworkError, isRetryableError, } from './shared/utils/errorUtils';
|
|
39
|
-
export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBackoffInterval, recordFailure, recordSuccess, shouldAllowRequest, delay, withRetry, } from './shared/utils/networkUtils';
|
|
40
|
-
export { isDev, debugLog, debugWarn, debugError, createDebugLogger, } from './shared/utils/debugUtils';
|
|
36
|
+
export { darkenColor, lightenColor, hexToRgb, rgbToHex, withOpacity, isLightColor, getContrastTextColor, } from './shared/utils/colorUtils.js';
|
|
37
|
+
export { normalizeTheme, normalizeColorScheme, getOppositeTheme, systemPrefersDarkMode, getSystemColorScheme, } from './shared/utils/themeUtils.js';
|
|
38
|
+
export { HttpStatus, getErrorStatus, getErrorMessage, isAlreadyRegisteredError, isUnauthorizedError, isForbiddenError, isNotFoundError, isRateLimitError, isServerError, isNetworkError, isRetryableError, } from './shared/utils/errorUtils.js';
|
|
39
|
+
export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBackoffInterval, recordFailure, recordSuccess, shouldAllowRequest, delay, withRetry, } from './shared/utils/networkUtils.js';
|
|
40
|
+
export { isDev, debugLog, debugWarn, debugError, createDebugLogger, } from './shared/utils/debugUtils.js';
|
|
41
41
|
// --- i18n ---
|
|
42
|
-
export { translate } from './i18n';
|
|
42
|
+
export { translate } from './i18n.js';
|
|
43
|
+
// --- Auth Helpers ---
|
|
44
|
+
export { SessionSyncRequiredError, AuthenticationFailedError, ensureValidToken, isAuthenticationError, withAuthErrorHandling, authenticatedApiCall, } from './utils/authHelpers.js';
|
|
43
45
|
// --- Session Utilities ---
|
|
44
|
-
export { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from './utils/sessionUtils';
|
|
46
|
+
export { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from './utils/sessionUtils.js';
|
|
45
47
|
// --- Constants ---
|
|
46
|
-
export { packageInfo } from './constants/version';
|
|
48
|
+
export { packageInfo } from './constants/version.js';
|
|
47
49
|
// --- API & Error Utilities ---
|
|
48
|
-
export * from './utils/apiUtils';
|
|
49
|
-
export { ErrorCodes, createApiError, handleHttpError, validateRequiredFields, } from './utils/errorUtils';
|
|
50
|
-
export { retryAsync } from './utils/asyncUtils';
|
|
51
|
-
export * from './utils/validationUtils';
|
|
52
|
-
export { logger, LogLevel, logAuth, logApi, logSession, logUser, logDevice, logPayment, logPerformance, } from './utils/loggerUtils';
|
|
50
|
+
export * from './utils/apiUtils.js';
|
|
51
|
+
export { ErrorCodes, createApiError, handleHttpError, validateRequiredFields, } from './utils/errorUtils.js';
|
|
52
|
+
export { retryAsync } from './utils/asyncUtils.js';
|
|
53
|
+
export * from './utils/validationUtils.js';
|
|
54
|
+
export { logger, LogLevel, logAuth, logApi, logSession, logUser, logDevice, logPayment, logPerformance, } from './utils/loggerUtils.js';
|
|
53
55
|
// Default export
|
|
54
|
-
import { OxyServices } from './OxyServices';
|
|
56
|
+
import { OxyServices } from './OxyServices.js';
|
|
55
57
|
export default OxyServices;
|