@oxyhq/core 1.0.2 → 1.2.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/dist/cjs/AuthManager.js +35 -10
- package/dist/cjs/CrossDomainAuth.js +2 -2
- package/dist/cjs/HttpService.js +40 -24
- package/dist/cjs/OxyServices.base.js +16 -3
- package/dist/cjs/crypto/keyManager.js +29 -24
- package/dist/cjs/crypto/polyfill.js +6 -1
- package/dist/cjs/crypto/signatureService.js +40 -31
- package/dist/cjs/i18n/index.js +36 -45
- package/dist/cjs/i18n/locales/ar-SA.json +114 -115
- package/dist/cjs/i18n/locales/ca-ES.json +114 -115
- package/dist/cjs/i18n/locales/de-DE.json +114 -115
- package/dist/cjs/i18n/locales/en-US.json +936 -936
- package/dist/cjs/i18n/locales/es-ES.json +924 -924
- package/dist/cjs/i18n/locales/fr-FR.json +114 -115
- package/dist/cjs/i18n/locales/it-IT.json +114 -115
- package/dist/cjs/i18n/locales/ja-JP.json +1 -1
- package/dist/cjs/i18n/locales/ko-KR.json +114 -115
- 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 +114 -115
- package/dist/cjs/i18n/locales/zh-CN.json +114 -115
- package/dist/cjs/mixins/OxyServices.fedcm.js +21 -45
- package/dist/cjs/mixins/OxyServices.language.js +5 -2
- package/dist/cjs/mixins/OxyServices.popup.js +16 -6
- package/dist/cjs/mixins/OxyServices.privacy.js +2 -1
- package/dist/cjs/mixins/OxyServices.redirect.js +16 -6
- package/dist/cjs/mixins/OxyServices.security.js +3 -2
- package/dist/cjs/shared/utils/debugUtils.js +8 -1
- package/dist/cjs/utils/deviceManager.js +4 -6
- package/dist/cjs/utils/platform.js +3 -2
- package/dist/esm/AuthManager.js +35 -10
- package/dist/esm/CrossDomainAuth.js +2 -2
- package/dist/esm/HttpService.js +40 -24
- package/dist/esm/OxyServices.base.js +16 -3
- package/dist/esm/crypto/keyManager.js +29 -24
- package/dist/esm/crypto/polyfill.js +6 -1
- package/dist/esm/crypto/signatureService.js +40 -31
- package/dist/esm/i18n/index.js +11 -23
- package/dist/esm/i18n/locales/ar-SA.json +114 -115
- package/dist/esm/i18n/locales/ca-ES.json +114 -115
- package/dist/esm/i18n/locales/de-DE.json +114 -115
- package/dist/esm/i18n/locales/en-US.json +936 -936
- package/dist/esm/i18n/locales/es-ES.json +924 -924
- package/dist/esm/i18n/locales/fr-FR.json +114 -115
- package/dist/esm/i18n/locales/it-IT.json +114 -115
- package/dist/esm/i18n/locales/ja-JP.json +1 -1
- package/dist/esm/i18n/locales/ko-KR.json +114 -115
- 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 +114 -115
- package/dist/esm/i18n/locales/zh-CN.json +114 -115
- package/dist/esm/mixins/OxyServices.fedcm.js +21 -45
- package/dist/esm/mixins/OxyServices.language.js +5 -2
- package/dist/esm/mixins/OxyServices.popup.js +16 -6
- package/dist/esm/mixins/OxyServices.privacy.js +2 -1
- package/dist/esm/mixins/OxyServices.redirect.js +16 -6
- package/dist/esm/mixins/OxyServices.security.js +3 -2
- package/dist/esm/shared/utils/debugUtils.js +8 -1
- package/dist/esm/utils/deviceManager.js +4 -6
- package/dist/esm/utils/platform.js +3 -2
- package/dist/types/AuthManager.d.ts +4 -1
- package/dist/types/CrossDomainAuth.d.ts +2 -2
- package/dist/types/HttpService.d.ts +2 -0
- package/dist/types/OxyServices.base.d.ts +4 -1
- package/dist/types/OxyServices.d.ts +13 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
- package/dist/types/mixins/OxyServices.features.d.ts +2 -0
- package/dist/types/mixins/OxyServices.fedcm.d.ts +4 -2
- package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
- package/dist/types/mixins/OxyServices.language.d.ts +2 -0
- package/dist/types/mixins/OxyServices.location.d.ts +2 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
- package/dist/types/mixins/OxyServices.security.d.ts +2 -0
- package/dist/types/mixins/OxyServices.user.d.ts +2 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +2 -0
- package/package.json +1 -2
- package/src/AuthManager.ts +42 -16
- package/src/CrossDomainAuth.ts +2 -2
- package/src/HttpService.ts +40 -26
- package/src/OxyServices.base.ts +21 -4
- package/src/OxyServices.ts +23 -2
- package/src/crypto/keyManager.ts +30 -25
- package/src/crypto/polyfill.ts +6 -1
- package/src/crypto/signatureService.ts +43 -37
- package/src/i18n/index.ts +33 -45
- package/src/index.ts +3 -0
- package/src/mixins/OxyServices.fedcm.ts +22 -48
- package/src/mixins/OxyServices.language.ts +6 -3
- package/src/mixins/OxyServices.popup.ts +16 -6
- package/src/mixins/OxyServices.privacy.ts +2 -1
- package/src/mixins/OxyServices.redirect.ts +16 -6
- package/src/mixins/OxyServices.security.ts +3 -2
- package/src/shared/utils/__tests__/debugUtils.test.ts +55 -0
- package/src/shared/utils/debugUtils.ts +6 -1
- package/src/utils/deviceManager.ts +5 -6
- package/src/utils/platform.ts +3 -2
|
@@ -147,7 +147,10 @@ export function OxyServicesFedCMMixin(Base) {
|
|
|
147
147
|
}
|
|
148
148
|
const clientId = this.getClientId();
|
|
149
149
|
debug.log('Silent SSO: Starting for', clientId);
|
|
150
|
-
//
|
|
150
|
+
// Only try silent mediation (no UI) - works if user previously consented.
|
|
151
|
+
// We intentionally do NOT fall back to optional mediation here because
|
|
152
|
+
// this runs on app startup — showing browser UI without user action is bad UX.
|
|
153
|
+
// Optional/interactive mediation should only happen when the user clicks "Sign In".
|
|
151
154
|
let credential = null;
|
|
152
155
|
try {
|
|
153
156
|
const nonce = this.generateNonce();
|
|
@@ -161,33 +164,13 @@ export function OxyServicesFedCMMixin(Base) {
|
|
|
161
164
|
debug.log('Silent SSO: Silent mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
|
|
162
165
|
}
|
|
163
166
|
catch (silentError) {
|
|
164
|
-
// Silent mediation failed - this is expected if user hasn't consented before or is in quiet period
|
|
165
167
|
const errorName = silentError instanceof Error ? silentError.name : 'Unknown';
|
|
166
168
|
const errorMessage = silentError instanceof Error ? silentError.message : String(silentError);
|
|
167
|
-
debug.log('Silent SSO: Silent mediation
|
|
168
|
-
|
|
169
|
-
// If silent failed, try optional mediation which shows browser UI if needed
|
|
170
|
-
if (!credential || !credential.token) {
|
|
171
|
-
try {
|
|
172
|
-
const nonce = this.generateNonce();
|
|
173
|
-
debug.log('Silent SSO: Trying optional mediation (may show browser UI)...');
|
|
174
|
-
credential = await this.requestIdentityCredential({
|
|
175
|
-
configURL: this.constructor.DEFAULT_CONFIG_URL,
|
|
176
|
-
clientId,
|
|
177
|
-
nonce,
|
|
178
|
-
mediation: 'optional',
|
|
179
|
-
});
|
|
180
|
-
debug.log('Silent SSO: Optional mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
|
|
181
|
-
}
|
|
182
|
-
catch (optionalError) {
|
|
183
|
-
const errorName = optionalError instanceof Error ? optionalError.name : 'Unknown';
|
|
184
|
-
const errorMessage = optionalError instanceof Error ? optionalError.message : String(optionalError);
|
|
185
|
-
debug.log('Silent SSO: Optional mediation also failed:', { name: errorName, message: errorMessage });
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
169
|
+
debug.log('Silent SSO: Silent mediation failed:', { name: errorName, message: errorMessage });
|
|
170
|
+
return null;
|
|
188
171
|
}
|
|
189
172
|
if (!credential || !credential.token) {
|
|
190
|
-
debug.log('Silent SSO: No credential returned (user
|
|
173
|
+
debug.log('Silent SSO: No credential returned (user not logged in at IdP or hasn\'t consented)');
|
|
191
174
|
return null;
|
|
192
175
|
}
|
|
193
176
|
debug.log('Silent SSO: Got credential, exchanging for session...');
|
|
@@ -337,28 +320,17 @@ export function OxyServicesFedCMMixin(Base) {
|
|
|
337
320
|
* @private
|
|
338
321
|
*/
|
|
339
322
|
async exchangeIdTokenForSession(idToken) {
|
|
340
|
-
debug.log('
|
|
341
|
-
debug.log('exchangeIdTokenForSession: Token length:', idToken?.length);
|
|
342
|
-
debug.log('exchangeIdTokenForSession: Token preview:', idToken?.substring(0, 50) + '...');
|
|
323
|
+
debug.log('Exchanging ID token for session...');
|
|
343
324
|
try {
|
|
344
325
|
const response = await this.makeRequest('POST', '/api/fedcm/exchange', { id_token: idToken }, { cache: false });
|
|
345
|
-
debug.log('
|
|
346
|
-
|
|
347
|
-
hasSessionId: !!response?.sessionId,
|
|
326
|
+
debug.log('Token exchange complete:', {
|
|
327
|
+
hasSession: !!response?.sessionId,
|
|
348
328
|
hasUser: !!response?.user,
|
|
349
|
-
hasAccessToken: !!response?.accessToken,
|
|
350
|
-
userId: response?.user?.id,
|
|
351
|
-
username: response?.user?.username,
|
|
352
|
-
responseKeys: response ? Object.keys(response) : [],
|
|
353
329
|
});
|
|
354
330
|
return response;
|
|
355
331
|
}
|
|
356
332
|
catch (error) {
|
|
357
|
-
debug.error('
|
|
358
|
-
name: error instanceof Error ? error.name : 'Unknown',
|
|
359
|
-
message: error instanceof Error ? error.message : String(error),
|
|
360
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
361
|
-
});
|
|
333
|
+
debug.error('Token exchange failed:', error instanceof Error ? error.message : String(error));
|
|
362
334
|
throw error;
|
|
363
335
|
}
|
|
364
336
|
}
|
|
@@ -404,11 +376,15 @@ export function OxyServicesFedCMMixin(Base) {
|
|
|
404
376
|
* @private
|
|
405
377
|
*/
|
|
406
378
|
generateNonce() {
|
|
407
|
-
if (typeof
|
|
408
|
-
return
|
|
379
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
380
|
+
return crypto.randomUUID();
|
|
381
|
+
}
|
|
382
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
383
|
+
const bytes = new Uint8Array(16);
|
|
384
|
+
crypto.getRandomValues(bytes);
|
|
385
|
+
return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
409
386
|
}
|
|
410
|
-
|
|
411
|
-
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
387
|
+
throw new Error('No secure random source available for nonce generation');
|
|
412
388
|
}
|
|
413
389
|
/**
|
|
414
390
|
* Get the client ID for this origin
|
|
@@ -423,9 +399,9 @@ export function OxyServicesFedCMMixin(Base) {
|
|
|
423
399
|
}
|
|
424
400
|
},
|
|
425
401
|
_a.DEFAULT_CONFIG_URL = 'https://auth.oxy.so/fedcm.json',
|
|
426
|
-
_a.FEDCM_TIMEOUT =
|
|
402
|
+
_a.FEDCM_TIMEOUT = 15000 // 15 seconds for interactive
|
|
427
403
|
,
|
|
428
|
-
_a.FEDCM_SILENT_TIMEOUT =
|
|
404
|
+
_a.FEDCM_SILENT_TIMEOUT = 3000 // 3 seconds for silent mediation
|
|
429
405
|
,
|
|
430
406
|
_a;
|
|
431
407
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Language Methods Mixin
|
|
3
3
|
*/
|
|
4
4
|
import { normalizeLanguageCode, getLanguageMetadata, getLanguageName, getNativeLanguageName } from '../utils/languageUtils';
|
|
5
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
5
6
|
export function OxyServicesLanguageMixin(Base) {
|
|
6
7
|
return class extends Base {
|
|
7
8
|
constructor(...args) {
|
|
@@ -14,7 +15,9 @@ export function OxyServicesLanguageMixin(Base) {
|
|
|
14
15
|
const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
15
16
|
if (isReactNative) {
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
19
|
+
const moduleName = '@react-native-async-storage/async-storage';
|
|
20
|
+
const asyncStorageModule = await import(moduleName);
|
|
18
21
|
const storage = asyncStorageModule.default;
|
|
19
22
|
return {
|
|
20
23
|
getItem: storage.getItem.bind(storage),
|
|
@@ -77,7 +80,7 @@ export function OxyServicesLanguageMixin(Base) {
|
|
|
77
80
|
return null;
|
|
78
81
|
}
|
|
79
82
|
catch (error) {
|
|
80
|
-
if (
|
|
83
|
+
if (isDev()) {
|
|
81
84
|
console.warn('Failed to get current language:', error);
|
|
82
85
|
}
|
|
83
86
|
return null;
|
|
@@ -319,10 +319,15 @@ export function OxyServicesPopupAuthMixin(Base) {
|
|
|
319
319
|
* @private
|
|
320
320
|
*/
|
|
321
321
|
generateState() {
|
|
322
|
-
if (typeof
|
|
323
|
-
return
|
|
322
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
323
|
+
return crypto.randomUUID();
|
|
324
324
|
}
|
|
325
|
-
|
|
325
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
326
|
+
const bytes = new Uint8Array(16);
|
|
327
|
+
crypto.getRandomValues(bytes);
|
|
328
|
+
return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
329
|
+
}
|
|
330
|
+
throw new Error('No secure random source available for state generation');
|
|
326
331
|
}
|
|
327
332
|
/**
|
|
328
333
|
* Generate nonce for replay attack prevention
|
|
@@ -330,10 +335,15 @@ export function OxyServicesPopupAuthMixin(Base) {
|
|
|
330
335
|
* @private
|
|
331
336
|
*/
|
|
332
337
|
generateNonce() {
|
|
333
|
-
if (typeof
|
|
334
|
-
return
|
|
338
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
339
|
+
return crypto.randomUUID();
|
|
340
|
+
}
|
|
341
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
342
|
+
const bytes = new Uint8Array(16);
|
|
343
|
+
crypto.getRandomValues(bytes);
|
|
344
|
+
return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
335
345
|
}
|
|
336
|
-
|
|
346
|
+
throw new Error('No secure random source available for nonce generation');
|
|
337
347
|
}
|
|
338
348
|
/**
|
|
339
349
|
* Store auth state in session storage
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
1
2
|
export function OxyServicesPrivacyMixin(Base) {
|
|
2
3
|
return class extends Base {
|
|
3
4
|
constructor(...args) {
|
|
@@ -25,7 +26,7 @@ export function OxyServicesPrivacyMixin(Base) {
|
|
|
25
26
|
}
|
|
26
27
|
catch (error) {
|
|
27
28
|
// If there's an error, assume not in list to avoid breaking functionality
|
|
28
|
-
if (
|
|
29
|
+
if (isDev()) {
|
|
29
30
|
console.warn('Error checking user list:', error);
|
|
30
31
|
}
|
|
31
32
|
return false;
|
|
@@ -250,10 +250,15 @@ export function OxyServicesRedirectAuthMixin(Base) {
|
|
|
250
250
|
* @private
|
|
251
251
|
*/
|
|
252
252
|
generateState() {
|
|
253
|
-
if (typeof
|
|
254
|
-
return
|
|
253
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
254
|
+
return crypto.randomUUID();
|
|
255
255
|
}
|
|
256
|
-
|
|
256
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
257
|
+
const bytes = new Uint8Array(16);
|
|
258
|
+
crypto.getRandomValues(bytes);
|
|
259
|
+
return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
260
|
+
}
|
|
261
|
+
throw new Error('No secure random source available for state generation');
|
|
257
262
|
}
|
|
258
263
|
/**
|
|
259
264
|
* Generate nonce for replay attack prevention
|
|
@@ -261,10 +266,15 @@ export function OxyServicesRedirectAuthMixin(Base) {
|
|
|
261
266
|
* @private
|
|
262
267
|
*/
|
|
263
268
|
generateNonce() {
|
|
264
|
-
if (typeof
|
|
265
|
-
return
|
|
269
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
270
|
+
return crypto.randomUUID();
|
|
271
|
+
}
|
|
272
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
273
|
+
const bytes = new Uint8Array(16);
|
|
274
|
+
crypto.getRandomValues(bytes);
|
|
275
|
+
return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
|
|
266
276
|
}
|
|
267
|
-
|
|
277
|
+
throw new Error('No secure random source available for nonce generation');
|
|
268
278
|
}
|
|
269
279
|
/**
|
|
270
280
|
* Store auth state in session storage
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
1
2
|
export function OxyServicesSecurityMixin(Base) {
|
|
2
3
|
return class extends Base {
|
|
3
4
|
constructor(...args) {
|
|
@@ -52,7 +53,7 @@ export function OxyServicesSecurityMixin(Base) {
|
|
|
52
53
|
catch (error) {
|
|
53
54
|
// Don't throw - logging failures shouldn't break user flow
|
|
54
55
|
// But log for monitoring
|
|
55
|
-
if (
|
|
56
|
+
if (isDev()) {
|
|
56
57
|
console.warn('[OxyServices] Failed to log private key exported event:', error);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -69,7 +70,7 @@ export function OxyServicesSecurityMixin(Base) {
|
|
|
69
70
|
catch (error) {
|
|
70
71
|
// Don't throw - logging failures shouldn't break user flow
|
|
71
72
|
// But log for monitoring
|
|
72
|
-
if (
|
|
73
|
+
if (isDev()) {
|
|
73
74
|
console.warn('[OxyServices] Failed to log backup created event:', error);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -10,7 +10,14 @@
|
|
|
10
10
|
* Check if running in development mode
|
|
11
11
|
*/
|
|
12
12
|
export const isDev = () => {
|
|
13
|
-
|
|
13
|
+
if (typeof __DEV__ !== 'undefined')
|
|
14
|
+
return __DEV__;
|
|
15
|
+
try {
|
|
16
|
+
return typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
14
21
|
};
|
|
15
22
|
/**
|
|
16
23
|
* Log a debug message (only in development)
|
|
@@ -15,7 +15,9 @@ export class DeviceManager {
|
|
|
15
15
|
static async getStorage() {
|
|
16
16
|
if (this.isReactNative()) {
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
19
|
+
const moduleName = '@react-native-async-storage/async-storage';
|
|
20
|
+
const asyncStorageModule = await import(moduleName);
|
|
19
21
|
const storage = asyncStorageModule.default;
|
|
20
22
|
return {
|
|
21
23
|
getItem: storage.getItem.bind(storage),
|
|
@@ -133,16 +135,12 @@ export class DeviceManager {
|
|
|
133
135
|
* Generate a unique device ID
|
|
134
136
|
*/
|
|
135
137
|
static generateDeviceId() {
|
|
136
|
-
// Use crypto.getRandomValues if available, otherwise fallback to Math.random
|
|
137
138
|
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
138
139
|
const array = new Uint8Array(32);
|
|
139
140
|
crypto.getRandomValues(array);
|
|
140
141
|
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
141
142
|
}
|
|
142
|
-
|
|
143
|
-
// Fallback for environments without crypto.getRandomValues
|
|
144
|
-
return 'device_' + Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
145
|
-
}
|
|
143
|
+
throw new Error('No secure random source available for device ID generation');
|
|
146
144
|
}
|
|
147
145
|
/**
|
|
148
146
|
* Get a user-friendly device name based on platform
|
|
@@ -92,8 +92,9 @@ export async function initPlatformFromReactNative() {
|
|
|
92
92
|
return; // Already initialized
|
|
93
93
|
}
|
|
94
94
|
try {
|
|
95
|
-
//
|
|
96
|
-
const
|
|
95
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
96
|
+
const moduleName = 'react-native';
|
|
97
|
+
const { Platform } = await import(moduleName);
|
|
97
98
|
setPlatformOS(Platform.OS);
|
|
98
99
|
}
|
|
99
100
|
catch {
|
|
@@ -66,6 +66,7 @@ export declare class AuthManager {
|
|
|
66
66
|
private listeners;
|
|
67
67
|
private currentUser;
|
|
68
68
|
private refreshTimer;
|
|
69
|
+
private refreshPromise;
|
|
69
70
|
private config;
|
|
70
71
|
constructor(oxyServices: OxyServices, config?: AuthManagerConfig);
|
|
71
72
|
/**
|
|
@@ -95,9 +96,11 @@ export declare class AuthManager {
|
|
|
95
96
|
*/
|
|
96
97
|
private setupTokenRefresh;
|
|
97
98
|
/**
|
|
98
|
-
* Refresh the access token.
|
|
99
|
+
* Refresh the access token. Deduplicates concurrent calls so only one
|
|
100
|
+
* refresh request is in-flight at a time.
|
|
99
101
|
*/
|
|
100
102
|
refreshToken(): Promise<boolean>;
|
|
103
|
+
private _doRefreshToken;
|
|
101
104
|
/**
|
|
102
105
|
* Sign out and clear all auth data.
|
|
103
106
|
*/
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Usage:
|
|
12
12
|
* ```typescript
|
|
13
|
-
* import { CrossDomainAuth } from '@oxyhq/
|
|
13
|
+
* import { CrossDomainAuth } from '@oxyhq/core';
|
|
14
14
|
*
|
|
15
15
|
* const auth = new CrossDomainAuth(oxyServices);
|
|
16
16
|
*
|
|
@@ -142,7 +142,7 @@ export declare class CrossDomainAuth {
|
|
|
142
142
|
*
|
|
143
143
|
* @example
|
|
144
144
|
* ```typescript
|
|
145
|
-
* import { createCrossDomainAuth } from '@oxyhq/
|
|
145
|
+
* import { createCrossDomainAuth } from '@oxyhq/core';
|
|
146
146
|
*
|
|
147
147
|
* const oxyServices = new OxyServices({ baseURL: 'https://api.oxy.so' });
|
|
148
148
|
* const auth = createCrossDomainAuth(oxyServices);
|
|
@@ -43,6 +43,7 @@ export declare class HttpService {
|
|
|
43
43
|
private requestQueue;
|
|
44
44
|
private logger;
|
|
45
45
|
private config;
|
|
46
|
+
private tokenRefreshPromise;
|
|
46
47
|
private requestMetrics;
|
|
47
48
|
constructor(config: OxyConfig);
|
|
48
49
|
/**
|
|
@@ -72,6 +73,7 @@ export declare class HttpService {
|
|
|
72
73
|
* Get auth header with automatic token refresh
|
|
73
74
|
*/
|
|
74
75
|
private getAuthHeader;
|
|
76
|
+
private _refreshTokenFromSession;
|
|
75
77
|
/**
|
|
76
78
|
* Unwrap standardized API response format
|
|
77
79
|
*/
|
|
@@ -66,8 +66,11 @@ export declare class OxyServicesBase {
|
|
|
66
66
|
* Clear stored authentication tokens
|
|
67
67
|
*/
|
|
68
68
|
clearTokens(): void;
|
|
69
|
+
/** @internal */ _cachedUserId: string | null | undefined;
|
|
70
|
+
/** @internal */ _cachedAccessToken: string | null;
|
|
69
71
|
/**
|
|
70
|
-
* Get the current user ID from the access token
|
|
72
|
+
* Get the current user ID from the access token.
|
|
73
|
+
* Caches the decoded value and invalidates when the token changes.
|
|
71
74
|
*/
|
|
72
75
|
getCurrentUserId(): string | null;
|
|
73
76
|
/**
|
|
@@ -58,12 +58,25 @@
|
|
|
58
58
|
*/
|
|
59
59
|
import { type OxyConfig } from './OxyServices.base';
|
|
60
60
|
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
61
|
+
import type { SessionLoginResponse } from './models/session';
|
|
62
|
+
import type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
|
|
63
|
+
import type { PopupAuthOptions } from './mixins/OxyServices.popup';
|
|
64
|
+
import type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
|
|
61
65
|
import { composeOxyServices } from './mixins';
|
|
62
66
|
declare const OxyServices_base: any;
|
|
63
67
|
export declare class OxyServices extends OxyServices_base {
|
|
64
68
|
constructor(config: OxyConfig);
|
|
65
69
|
}
|
|
66
70
|
export interface OxyServices extends InstanceType<ReturnType<typeof composeOxyServices>> {
|
|
71
|
+
isFedCMSupported(): boolean;
|
|
72
|
+
signInWithFedCM(options?: FedCMAuthOptions): Promise<SessionLoginResponse>;
|
|
73
|
+
silentSignInWithFedCM(): Promise<SessionLoginResponse | null>;
|
|
74
|
+
revokeFedCMCredential(): Promise<void>;
|
|
75
|
+
getFedCMConfig(): FedCMConfig;
|
|
76
|
+
signInWithPopup(options?: PopupAuthOptions): Promise<SessionLoginResponse>;
|
|
77
|
+
signUpWithPopup(options?: PopupAuthOptions): Promise<SessionLoginResponse>;
|
|
78
|
+
signInWithRedirect(options?: RedirectAuthOptions): void;
|
|
79
|
+
signUpWithRedirect(options?: RedirectAuthOptions): void;
|
|
67
80
|
}
|
|
68
81
|
export { OxyAuthenticationError, OxyAuthenticationTimeoutError };
|
|
69
82
|
/**
|
package/dist/types/index.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export { AuthManager, createAuthManager } from './AuthManager';
|
|
|
20
20
|
export type { StorageAdapter, AuthStateChangeCallback, AuthMethod, AuthManagerConfig } from './AuthManager';
|
|
21
21
|
export { CrossDomainAuth, createCrossDomainAuth } from './CrossDomainAuth';
|
|
22
22
|
export type { CrossDomainAuthOptions } from './CrossDomainAuth';
|
|
23
|
+
export type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
|
|
24
|
+
export type { PopupAuthOptions } from './mixins/OxyServices.popup';
|
|
25
|
+
export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
|
|
23
26
|
export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
|
|
24
27
|
export type { KeyPair, SignedMessage, AuthChallenge, RecoveryPhraseResult } from './crypto';
|
|
25
28
|
export * from './models/interfaces';
|
|
@@ -44,6 +44,8 @@ export declare function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBa
|
|
|
44
44
|
getCloudURL(): string;
|
|
45
45
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
46
46
|
clearTokens(): void;
|
|
47
|
+
_cachedUserId: string | null | undefined;
|
|
48
|
+
_cachedAccessToken: string | null;
|
|
47
49
|
getCurrentUserId(): string | null;
|
|
48
50
|
hasValidToken(): boolean;
|
|
49
51
|
getAccessToken(): string | null;
|
|
@@ -113,6 +113,8 @@ export declare function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>
|
|
|
113
113
|
getCloudURL(): string;
|
|
114
114
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
115
115
|
clearTokens(): void;
|
|
116
|
+
_cachedUserId: string | null | undefined;
|
|
117
|
+
_cachedAccessToken: string | null;
|
|
116
118
|
getCurrentUserId(): string | null;
|
|
117
119
|
hasValidToken(): boolean;
|
|
118
120
|
getAccessToken(): string | null;
|
|
@@ -164,6 +164,8 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
164
164
|
getCloudURL(): string;
|
|
165
165
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
166
166
|
clearTokens(): void;
|
|
167
|
+
_cachedUserId: string | null | undefined;
|
|
168
|
+
_cachedAccessToken: string | null;
|
|
167
169
|
getCurrentUserId(): string | null;
|
|
168
170
|
hasValidToken(): boolean;
|
|
169
171
|
getAccessToken(): string | null;
|
|
@@ -77,6 +77,8 @@ export declare function OxyServicesDeveloperMixin<T extends typeof OxyServicesBa
|
|
|
77
77
|
getCloudURL(): string;
|
|
78
78
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
79
79
|
clearTokens(): void;
|
|
80
|
+
_cachedUserId: string | null | undefined;
|
|
81
|
+
_cachedAccessToken: string | null;
|
|
80
82
|
getCurrentUserId(): string | null;
|
|
81
83
|
hasValidToken(): boolean;
|
|
82
84
|
getAccessToken(): string | null;
|
|
@@ -74,6 +74,8 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
|
|
|
74
74
|
getCloudURL(): string;
|
|
75
75
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
76
76
|
clearTokens(): void;
|
|
77
|
+
_cachedUserId: string | null | undefined;
|
|
78
|
+
_cachedAccessToken: string | null;
|
|
77
79
|
getCurrentUserId(): string | null;
|
|
78
80
|
hasValidToken(): boolean;
|
|
79
81
|
getAccessToken(): string | null;
|
|
@@ -206,6 +206,8 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
|
|
|
206
206
|
getCloudURL(): string;
|
|
207
207
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
208
208
|
clearTokens(): void;
|
|
209
|
+
_cachedUserId: string | null | undefined;
|
|
210
|
+
_cachedAccessToken: string | null;
|
|
209
211
|
getCurrentUserId(): string | null;
|
|
210
212
|
hasValidToken(): boolean;
|
|
211
213
|
getAccessToken(): string | null;
|
|
@@ -170,6 +170,8 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
|
|
|
170
170
|
getCloudURL(): string;
|
|
171
171
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
172
172
|
clearTokens(): void;
|
|
173
|
+
_cachedUserId: string | null | undefined;
|
|
174
|
+
_cachedAccessToken: string | null;
|
|
173
175
|
getCurrentUserId(): string | null;
|
|
174
176
|
hasValidToken(): boolean;
|
|
175
177
|
getAccessToken(): string | null;
|
|
@@ -189,8 +191,8 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
|
|
|
189
191
|
}>;
|
|
190
192
|
};
|
|
191
193
|
readonly DEFAULT_CONFIG_URL: "https://auth.oxy.so/fedcm.json";
|
|
192
|
-
readonly FEDCM_TIMEOUT:
|
|
193
|
-
readonly FEDCM_SILENT_TIMEOUT:
|
|
194
|
+
readonly FEDCM_TIMEOUT: 15000;
|
|
195
|
+
readonly FEDCM_SILENT_TIMEOUT: 3000;
|
|
194
196
|
/**
|
|
195
197
|
* Check if FedCM is supported in the current browser
|
|
196
198
|
*/
|
|
@@ -63,6 +63,8 @@ export declare function OxyServicesKarmaMixin<T extends typeof OxyServicesBase>(
|
|
|
63
63
|
getCloudURL(): string;
|
|
64
64
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
65
65
|
clearTokens(): void;
|
|
66
|
+
_cachedUserId: string | null | undefined;
|
|
67
|
+
_cachedAccessToken: string | null;
|
|
66
68
|
getCurrentUserId(): string | null;
|
|
67
69
|
hasValidToken(): boolean;
|
|
68
70
|
getAccessToken(): string | null;
|
|
@@ -59,6 +59,8 @@ export declare function OxyServicesLanguageMixin<T extends typeof OxyServicesBas
|
|
|
59
59
|
getCloudURL(): string;
|
|
60
60
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
61
61
|
clearTokens(): void;
|
|
62
|
+
_cachedUserId: string | null | undefined;
|
|
63
|
+
_cachedAccessToken: string | null;
|
|
62
64
|
getCurrentUserId(): string | null;
|
|
63
65
|
hasValidToken(): boolean;
|
|
64
66
|
getAccessToken(): string | null;
|
|
@@ -42,6 +42,8 @@ export declare function OxyServicesLocationMixin<T extends typeof OxyServicesBas
|
|
|
42
42
|
getCloudURL(): string;
|
|
43
43
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
44
44
|
clearTokens(): void;
|
|
45
|
+
_cachedUserId: string | null | undefined;
|
|
46
|
+
_cachedAccessToken: string | null;
|
|
45
47
|
getCurrentUserId(): string | null;
|
|
46
48
|
hasValidToken(): boolean;
|
|
47
49
|
getAccessToken(): string | null;
|
|
@@ -89,6 +89,8 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
|
|
|
89
89
|
getCloudURL(): string;
|
|
90
90
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
91
91
|
clearTokens(): void;
|
|
92
|
+
_cachedUserId: string | null | undefined;
|
|
93
|
+
_cachedAccessToken: string | null;
|
|
92
94
|
getCurrentUserId(): string | null;
|
|
93
95
|
hasValidToken(): boolean;
|
|
94
96
|
getAccessToken(): string | null;
|
|
@@ -177,6 +177,8 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
|
|
|
177
177
|
getCloudURL(): string;
|
|
178
178
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
179
179
|
clearTokens(): void;
|
|
180
|
+
_cachedUserId: string | null | undefined;
|
|
181
|
+
_cachedAccessToken: string | null;
|
|
180
182
|
getCurrentUserId(): string | null;
|
|
181
183
|
hasValidToken(): boolean;
|
|
182
184
|
getAccessToken(): string | null;
|
|
@@ -100,6 +100,8 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
|
|
|
100
100
|
getCloudURL(): string;
|
|
101
101
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
102
102
|
clearTokens(): void;
|
|
103
|
+
_cachedUserId: string | null | undefined;
|
|
104
|
+
_cachedAccessToken: string | null;
|
|
103
105
|
getCurrentUserId(): string | null;
|
|
104
106
|
hasValidToken(): boolean;
|
|
105
107
|
getAccessToken(): string | null;
|
|
@@ -216,6 +216,8 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
|
|
|
216
216
|
getCloudURL(): string;
|
|
217
217
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
218
218
|
clearTokens(): void;
|
|
219
|
+
_cachedUserId: string | null | undefined;
|
|
220
|
+
_cachedAccessToken: string | null;
|
|
219
221
|
getCurrentUserId(): string | null;
|
|
220
222
|
hasValidToken(): boolean;
|
|
221
223
|
getAccessToken(): string | null;
|
|
@@ -56,6 +56,8 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
|
|
|
56
56
|
getCloudURL(): string;
|
|
57
57
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
58
58
|
clearTokens(): void;
|
|
59
|
+
_cachedUserId: string | null | undefined;
|
|
60
|
+
_cachedAccessToken: string | null;
|
|
59
61
|
getCurrentUserId(): string | null;
|
|
60
62
|
hasValidToken(): boolean;
|
|
61
63
|
getAccessToken(): string | null;
|
|
@@ -160,6 +160,8 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
160
160
|
getCloudURL(): string;
|
|
161
161
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
162
162
|
clearTokens(): void;
|
|
163
|
+
_cachedUserId: string | null | undefined;
|
|
164
|
+
_cachedAccessToken: string | null;
|
|
163
165
|
getCurrentUserId(): string | null;
|
|
164
166
|
hasValidToken(): boolean;
|
|
165
167
|
getAccessToken(): string | null;
|
|
@@ -71,6 +71,8 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
|
|
|
71
71
|
getCloudURL(): string;
|
|
72
72
|
setTokens(accessToken: string, refreshToken?: string): void;
|
|
73
73
|
clearTokens(): void;
|
|
74
|
+
_cachedUserId: string | null | undefined;
|
|
75
|
+
_cachedAccessToken: string | null;
|
|
74
76
|
getCurrentUserId(): string | null;
|
|
75
77
|
hasValidToken(): boolean;
|
|
76
78
|
getAccessToken(): string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
"lint": "biome lint --error-on-warnings ./src"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"axios": "^1.9.0",
|
|
69
68
|
"bip39": "^3.1.0",
|
|
70
69
|
"buffer": "^6.0.3",
|
|
71
70
|
"elliptic": "^6.6.1",
|