@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.
Files changed (122) hide show
  1. package/dist/cjs/AuthManager.js +35 -10
  2. package/dist/cjs/CrossDomainAuth.js +2 -2
  3. package/dist/cjs/HttpService.js +40 -24
  4. package/dist/cjs/OxyServices.base.js +16 -3
  5. package/dist/cjs/crypto/keyManager.js +29 -24
  6. package/dist/cjs/crypto/polyfill.js +6 -1
  7. package/dist/cjs/crypto/signatureService.js +40 -31
  8. package/dist/cjs/i18n/index.js +36 -45
  9. package/dist/cjs/i18n/locales/ar-SA.json +114 -115
  10. package/dist/cjs/i18n/locales/ca-ES.json +114 -115
  11. package/dist/cjs/i18n/locales/de-DE.json +114 -115
  12. package/dist/cjs/i18n/locales/en-US.json +936 -936
  13. package/dist/cjs/i18n/locales/es-ES.json +924 -924
  14. package/dist/cjs/i18n/locales/fr-FR.json +114 -115
  15. package/dist/cjs/i18n/locales/it-IT.json +114 -115
  16. package/dist/cjs/i18n/locales/ja-JP.json +1 -1
  17. package/dist/cjs/i18n/locales/ko-KR.json +114 -115
  18. package/dist/cjs/i18n/locales/locales/ar-SA.json +120 -0
  19. package/dist/cjs/i18n/locales/locales/ca-ES.json +120 -0
  20. package/dist/cjs/i18n/locales/locales/de-DE.json +120 -0
  21. package/dist/cjs/i18n/locales/locales/en-US.json +956 -0
  22. package/dist/cjs/i18n/locales/locales/es-ES.json +944 -0
  23. package/dist/cjs/i18n/locales/locales/fr-FR.json +120 -0
  24. package/dist/cjs/i18n/locales/locales/it-IT.json +120 -0
  25. package/dist/cjs/i18n/locales/locales/ja-JP.json +119 -0
  26. package/dist/cjs/i18n/locales/locales/ko-KR.json +120 -0
  27. package/dist/cjs/i18n/locales/locales/pt-PT.json +120 -0
  28. package/dist/cjs/i18n/locales/locales/zh-CN.json +120 -0
  29. package/dist/cjs/i18n/locales/pt-PT.json +114 -115
  30. package/dist/cjs/i18n/locales/zh-CN.json +114 -115
  31. package/dist/cjs/mixins/OxyServices.fedcm.js +21 -45
  32. package/dist/cjs/mixins/OxyServices.language.js +5 -2
  33. package/dist/cjs/mixins/OxyServices.popup.js +16 -6
  34. package/dist/cjs/mixins/OxyServices.privacy.js +2 -1
  35. package/dist/cjs/mixins/OxyServices.redirect.js +16 -6
  36. package/dist/cjs/mixins/OxyServices.security.js +3 -2
  37. package/dist/cjs/shared/utils/debugUtils.js +8 -1
  38. package/dist/cjs/utils/deviceManager.js +4 -6
  39. package/dist/cjs/utils/platform.js +3 -2
  40. package/dist/esm/AuthManager.js +35 -10
  41. package/dist/esm/CrossDomainAuth.js +2 -2
  42. package/dist/esm/HttpService.js +40 -24
  43. package/dist/esm/OxyServices.base.js +16 -3
  44. package/dist/esm/crypto/keyManager.js +29 -24
  45. package/dist/esm/crypto/polyfill.js +6 -1
  46. package/dist/esm/crypto/signatureService.js +40 -31
  47. package/dist/esm/i18n/index.js +11 -23
  48. package/dist/esm/i18n/locales/ar-SA.json +114 -115
  49. package/dist/esm/i18n/locales/ca-ES.json +114 -115
  50. package/dist/esm/i18n/locales/de-DE.json +114 -115
  51. package/dist/esm/i18n/locales/en-US.json +936 -936
  52. package/dist/esm/i18n/locales/es-ES.json +924 -924
  53. package/dist/esm/i18n/locales/fr-FR.json +114 -115
  54. package/dist/esm/i18n/locales/it-IT.json +114 -115
  55. package/dist/esm/i18n/locales/ja-JP.json +1 -1
  56. package/dist/esm/i18n/locales/ko-KR.json +114 -115
  57. package/dist/esm/i18n/locales/locales/ar-SA.json +120 -0
  58. package/dist/esm/i18n/locales/locales/ca-ES.json +120 -0
  59. package/dist/esm/i18n/locales/locales/de-DE.json +120 -0
  60. package/dist/esm/i18n/locales/locales/en-US.json +956 -0
  61. package/dist/esm/i18n/locales/locales/es-ES.json +944 -0
  62. package/dist/esm/i18n/locales/locales/fr-FR.json +120 -0
  63. package/dist/esm/i18n/locales/locales/it-IT.json +120 -0
  64. package/dist/esm/i18n/locales/locales/ja-JP.json +119 -0
  65. package/dist/esm/i18n/locales/locales/ko-KR.json +120 -0
  66. package/dist/esm/i18n/locales/locales/pt-PT.json +120 -0
  67. package/dist/esm/i18n/locales/locales/zh-CN.json +120 -0
  68. package/dist/esm/i18n/locales/pt-PT.json +114 -115
  69. package/dist/esm/i18n/locales/zh-CN.json +114 -115
  70. package/dist/esm/mixins/OxyServices.fedcm.js +21 -45
  71. package/dist/esm/mixins/OxyServices.language.js +5 -2
  72. package/dist/esm/mixins/OxyServices.popup.js +16 -6
  73. package/dist/esm/mixins/OxyServices.privacy.js +2 -1
  74. package/dist/esm/mixins/OxyServices.redirect.js +16 -6
  75. package/dist/esm/mixins/OxyServices.security.js +3 -2
  76. package/dist/esm/shared/utils/debugUtils.js +8 -1
  77. package/dist/esm/utils/deviceManager.js +4 -6
  78. package/dist/esm/utils/platform.js +3 -2
  79. package/dist/types/AuthManager.d.ts +4 -1
  80. package/dist/types/CrossDomainAuth.d.ts +2 -2
  81. package/dist/types/HttpService.d.ts +2 -0
  82. package/dist/types/OxyServices.base.d.ts +4 -1
  83. package/dist/types/OxyServices.d.ts +13 -0
  84. package/dist/types/index.d.ts +3 -0
  85. package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
  86. package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
  87. package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
  88. package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
  89. package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
  90. package/dist/types/mixins/OxyServices.features.d.ts +2 -0
  91. package/dist/types/mixins/OxyServices.fedcm.d.ts +4 -2
  92. package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
  93. package/dist/types/mixins/OxyServices.language.d.ts +2 -0
  94. package/dist/types/mixins/OxyServices.location.d.ts +2 -0
  95. package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
  96. package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
  97. package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
  98. package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
  99. package/dist/types/mixins/OxyServices.security.d.ts +2 -0
  100. package/dist/types/mixins/OxyServices.user.d.ts +2 -0
  101. package/dist/types/mixins/OxyServices.utility.d.ts +2 -0
  102. package/package.json +1 -2
  103. package/src/AuthManager.ts +42 -16
  104. package/src/CrossDomainAuth.ts +2 -2
  105. package/src/HttpService.ts +40 -26
  106. package/src/OxyServices.base.ts +21 -4
  107. package/src/OxyServices.ts +23 -2
  108. package/src/crypto/keyManager.ts +30 -25
  109. package/src/crypto/polyfill.ts +6 -1
  110. package/src/crypto/signatureService.ts +43 -37
  111. package/src/i18n/index.ts +33 -45
  112. package/src/index.ts +3 -0
  113. package/src/mixins/OxyServices.fedcm.ts +22 -48
  114. package/src/mixins/OxyServices.language.ts +6 -3
  115. package/src/mixins/OxyServices.popup.ts +16 -6
  116. package/src/mixins/OxyServices.privacy.ts +2 -1
  117. package/src/mixins/OxyServices.redirect.ts +16 -6
  118. package/src/mixins/OxyServices.security.ts +3 -2
  119. package/src/shared/utils/__tests__/debugUtils.test.ts +55 -0
  120. package/src/shared/utils/debugUtils.ts +6 -1
  121. package/src/utils/deviceManager.ts +5 -6
  122. 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
- // First try silent mediation (no UI) - works if user previously consented
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 error (will try optional):', { name: errorName, message: errorMessage });
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 may have dismissed prompt or is not logged in at IdP)');
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('exchangeIdTokenForSession: Starting exchange...');
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('exchangeIdTokenForSession: Response received:', {
346
- hasResponse: !!response,
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('exchangeIdTokenForSession: 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 window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
408
- return window.crypto.randomUUID();
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
- // Fallback for older browsers
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 = 60000 // 1 minute for interactive
402
+ _a.FEDCM_TIMEOUT = 15000 // 15 seconds for interactive
427
403
  ,
428
- _a.FEDCM_SILENT_TIMEOUT = 10000 // 10 seconds for silent mediation
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
- const asyncStorageModule = await import('@react-native-async-storage/async-storage');
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 (__DEV__) {
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 window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
323
- return window.crypto.randomUUID();
322
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
323
+ return crypto.randomUUID();
324
324
  }
325
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
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 window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
334
- return window.crypto.randomUUID();
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
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
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 (__DEV__) {
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 window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
254
- return window.crypto.randomUUID();
253
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
254
+ return crypto.randomUUID();
255
255
  }
256
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
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 window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
265
- return window.crypto.randomUUID();
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
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
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 (__DEV__) {
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 (__DEV__) {
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
- return typeof __DEV__ !== 'undefined' && __DEV__;
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
- const asyncStorageModule = await import('@react-native-async-storage/async-storage');
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
- else {
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
- // Dynamic import to avoid bundler issues
96
- const { Platform } = await import('react-native');
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/services';
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/services';
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
  /**
@@ -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: 60000;
193
- readonly FEDCM_SILENT_TIMEOUT: 10000;
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.2",
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",