@oxyhq/core 3.4.0 → 3.4.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.
Files changed (174) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/AuthManager.js +91 -319
  3. package/dist/cjs/CrossDomainAuth.js +19 -106
  4. package/dist/cjs/HttpService.js +49 -73
  5. package/dist/cjs/OxyServices.base.js +2 -2
  6. package/dist/cjs/i18n/index.js +7 -1
  7. package/dist/cjs/i18n/locales/ar-SA.json +18 -2
  8. package/dist/cjs/i18n/locales/ca-ES.json +18 -2
  9. package/dist/cjs/i18n/locales/de-DE.json +18 -2
  10. package/dist/cjs/i18n/locales/en-US.json +16 -2
  11. package/dist/cjs/i18n/locales/es-ES.json +16 -2
  12. package/dist/cjs/i18n/locales/fr-FR.json +18 -2
  13. package/dist/cjs/i18n/locales/it-IT.json +18 -2
  14. package/dist/cjs/i18n/locales/ja-JP.json +18 -2
  15. package/dist/cjs/i18n/locales/ko-KR.json +18 -2
  16. package/dist/cjs/i18n/locales/locales/ar-SA.json +18 -2
  17. package/dist/cjs/i18n/locales/locales/ca-ES.json +18 -2
  18. package/dist/cjs/i18n/locales/locales/de-DE.json +18 -2
  19. package/dist/cjs/i18n/locales/locales/en-US.json +17 -3
  20. package/dist/cjs/i18n/locales/locales/es-ES.json +16 -2
  21. package/dist/cjs/i18n/locales/locales/fr-FR.json +18 -2
  22. package/dist/cjs/i18n/locales/locales/it-IT.json +18 -2
  23. package/dist/cjs/i18n/locales/locales/ja-JP.json +18 -2
  24. package/dist/cjs/i18n/locales/locales/ko-KR.json +18 -2
  25. package/dist/cjs/i18n/locales/locales/pt-PT.json +18 -2
  26. package/dist/cjs/i18n/locales/locales/zh-CN.json +18 -2
  27. package/dist/cjs/i18n/locales/pt-PT.json +18 -2
  28. package/dist/cjs/i18n/locales/zh-CN.json +18 -2
  29. package/dist/cjs/mixins/OxyServices.auth.js +20 -63
  30. package/dist/cjs/mixins/OxyServices.fedcm.js +10 -12
  31. package/dist/cjs/mixins/OxyServices.popup.js +50 -299
  32. package/dist/cjs/mixins/OxyServices.redirect.js +84 -348
  33. package/dist/cjs/mixins/OxyServices.silent.js +204 -0
  34. package/dist/cjs/mixins/OxyServices.sso.js +4 -5
  35. package/dist/cjs/mixins/OxyServices.utility.js +6 -15
  36. package/dist/cjs/mixins/index.js +5 -6
  37. package/dist/cjs/server/index.js +21 -0
  38. package/dist/cjs/server/rateLimit.js +77 -0
  39. package/dist/cjs/shared/utils/debugUtils.js +1 -1
  40. package/dist/cjs/utils/accountUtils.js +4 -4
  41. package/dist/cjs/utils/authHelpers.js +21 -15
  42. package/dist/cjs/utils/coldBoot.js +3 -3
  43. package/dist/cjs/utils/fapiAutoDetect.js +1 -1
  44. package/dist/esm/.tsbuildinfo +1 -1
  45. package/dist/esm/AuthManager.js +91 -319
  46. package/dist/esm/CrossDomainAuth.js +19 -106
  47. package/dist/esm/HttpService.js +49 -73
  48. package/dist/esm/OxyServices.base.js +2 -2
  49. package/dist/esm/i18n/index.js +7 -1
  50. package/dist/esm/i18n/locales/ar-SA.json +18 -2
  51. package/dist/esm/i18n/locales/ca-ES.json +18 -2
  52. package/dist/esm/i18n/locales/de-DE.json +18 -2
  53. package/dist/esm/i18n/locales/en-US.json +16 -2
  54. package/dist/esm/i18n/locales/es-ES.json +16 -2
  55. package/dist/esm/i18n/locales/fr-FR.json +18 -2
  56. package/dist/esm/i18n/locales/it-IT.json +18 -2
  57. package/dist/esm/i18n/locales/ja-JP.json +18 -2
  58. package/dist/esm/i18n/locales/ko-KR.json +18 -2
  59. package/dist/esm/i18n/locales/locales/ar-SA.json +18 -2
  60. package/dist/esm/i18n/locales/locales/ca-ES.json +18 -2
  61. package/dist/esm/i18n/locales/locales/de-DE.json +18 -2
  62. package/dist/esm/i18n/locales/locales/en-US.json +17 -3
  63. package/dist/esm/i18n/locales/locales/es-ES.json +16 -2
  64. package/dist/esm/i18n/locales/locales/fr-FR.json +18 -2
  65. package/dist/esm/i18n/locales/locales/it-IT.json +18 -2
  66. package/dist/esm/i18n/locales/locales/ja-JP.json +18 -2
  67. package/dist/esm/i18n/locales/locales/ko-KR.json +18 -2
  68. package/dist/esm/i18n/locales/locales/pt-PT.json +18 -2
  69. package/dist/esm/i18n/locales/locales/zh-CN.json +18 -2
  70. package/dist/esm/i18n/locales/pt-PT.json +18 -2
  71. package/dist/esm/i18n/locales/zh-CN.json +18 -2
  72. package/dist/esm/mixins/OxyServices.auth.js +20 -63
  73. package/dist/esm/mixins/OxyServices.fedcm.js +10 -12
  74. package/dist/esm/mixins/OxyServices.popup.js +52 -301
  75. package/dist/esm/mixins/OxyServices.redirect.js +84 -349
  76. package/dist/esm/mixins/OxyServices.silent.js +202 -0
  77. package/dist/esm/mixins/OxyServices.sso.js +4 -5
  78. package/dist/esm/mixins/OxyServices.utility.js +6 -15
  79. package/dist/esm/mixins/index.js +5 -6
  80. package/dist/esm/server/index.js +17 -0
  81. package/dist/esm/server/rateLimit.js +71 -0
  82. package/dist/esm/shared/utils/debugUtils.js +1 -1
  83. package/dist/esm/utils/accountUtils.js +4 -4
  84. package/dist/esm/utils/authHelpers.js +21 -15
  85. package/dist/esm/utils/coldBoot.js +3 -3
  86. package/dist/esm/utils/fapiAutoDetect.js +1 -1
  87. package/dist/types/.tsbuildinfo +1 -1
  88. package/dist/types/AuthManager.d.ts +26 -53
  89. package/dist/types/AuthManagerTypes.d.ts +5 -9
  90. package/dist/types/CrossDomainAuth.d.ts +13 -52
  91. package/dist/types/HttpService.d.ts +9 -8
  92. package/dist/types/OxyServices.base.d.ts +1 -1
  93. package/dist/types/OxyServices.d.ts +4 -10
  94. package/dist/types/index.d.ts +1 -1
  95. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -1
  96. package/dist/types/mixins/OxyServices.appData.d.ts +1 -1
  97. package/dist/types/mixins/OxyServices.applications.d.ts +1 -1
  98. package/dist/types/mixins/OxyServices.assets.d.ts +1 -1
  99. package/dist/types/mixins/OxyServices.auth.d.ts +10 -31
  100. package/dist/types/mixins/OxyServices.contacts.d.ts +1 -1
  101. package/dist/types/mixins/OxyServices.devices.d.ts +1 -1
  102. package/dist/types/mixins/OxyServices.features.d.ts +1 -1
  103. package/dist/types/mixins/OxyServices.fedcm.d.ts +5 -5
  104. package/dist/types/mixins/OxyServices.language.d.ts +1 -1
  105. package/dist/types/mixins/OxyServices.location.d.ts +1 -1
  106. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -1
  107. package/dist/types/mixins/OxyServices.payment.d.ts +1 -1
  108. package/dist/types/mixins/OxyServices.popup.d.ts +18 -120
  109. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -1
  110. package/dist/types/mixins/OxyServices.redirect.d.ts +13 -174
  111. package/dist/types/mixins/OxyServices.reputation.d.ts +1 -1
  112. package/dist/types/mixins/OxyServices.security.d.ts +1 -1
  113. package/dist/types/mixins/OxyServices.silent.d.ts +131 -0
  114. package/dist/types/mixins/OxyServices.sso.d.ts +4 -5
  115. package/dist/types/mixins/OxyServices.topics.d.ts +1 -1
  116. package/dist/types/mixins/OxyServices.user.d.ts +1 -1
  117. package/dist/types/mixins/OxyServices.utility.d.ts +3 -8
  118. package/dist/types/mixins/OxyServices.workspaces.d.ts +1 -1
  119. package/dist/types/mixins/index.d.ts +3 -3
  120. package/dist/types/models/interfaces.d.ts +5 -16
  121. package/dist/types/models/session.d.ts +0 -2
  122. package/dist/types/server/index.d.ts +18 -0
  123. package/dist/types/server/rateLimit.d.ts +40 -0
  124. package/dist/types/shared/utils/debugUtils.d.ts +1 -1
  125. package/dist/types/utils/authHelpers.d.ts +4 -3
  126. package/dist/types/utils/coldBoot.d.ts +2 -2
  127. package/dist/types/utils/fapiAutoDetect.d.ts +1 -1
  128. package/package.json +25 -3
  129. package/src/AuthManager.ts +100 -370
  130. package/src/AuthManagerTypes.ts +5 -9
  131. package/src/CrossDomainAuth.ts +22 -129
  132. package/src/HttpService.ts +55 -73
  133. package/src/OxyServices.base.ts +2 -3
  134. package/src/OxyServices.ts +9 -11
  135. package/src/__tests__/authManager.cookiePath.test.ts +19 -17
  136. package/src/__tests__/authManager.security.test.ts +7 -3
  137. package/src/__tests__/crossDomainAuth.test.ts +26 -118
  138. package/src/i18n/index.ts +7 -1
  139. package/src/i18n/locales/ar-SA.json +18 -2
  140. package/src/i18n/locales/ca-ES.json +18 -2
  141. package/src/i18n/locales/de-DE.json +18 -2
  142. package/src/i18n/locales/en-US.json +17 -3
  143. package/src/i18n/locales/es-ES.json +16 -2
  144. package/src/i18n/locales/fr-FR.json +18 -2
  145. package/src/i18n/locales/it-IT.json +18 -2
  146. package/src/i18n/locales/ja-JP.json +18 -2
  147. package/src/i18n/locales/ko-KR.json +18 -2
  148. package/src/i18n/locales/pt-PT.json +18 -2
  149. package/src/i18n/locales/zh-CN.json +18 -2
  150. package/src/index.ts +1 -1
  151. package/src/mixins/OxyServices.auth.ts +23 -75
  152. package/src/mixins/OxyServices.fedcm.ts +10 -12
  153. package/src/mixins/OxyServices.redirect.ts +82 -371
  154. package/src/mixins/OxyServices.silent.ts +272 -0
  155. package/src/mixins/OxyServices.sso.ts +5 -6
  156. package/src/mixins/OxyServices.utility.ts +9 -22
  157. package/src/mixins/__tests__/appData.test.ts +1 -1
  158. package/src/mixins/__tests__/onTokensChanged.test.ts +1 -1
  159. package/src/mixins/__tests__/reputation.test.ts +1 -1
  160. package/src/mixins/__tests__/serviceAuth.test.ts +7 -5
  161. package/src/mixins/__tests__/silent.test.ts +102 -0
  162. package/src/mixins/__tests__/verifyChallenge.test.ts +9 -14
  163. package/src/mixins/index.ts +6 -8
  164. package/src/models/interfaces.ts +5 -16
  165. package/src/models/session.ts +1 -3
  166. package/src/server/index.ts +19 -0
  167. package/src/server/rateLimit.ts +170 -0
  168. package/src/shared/utils/debugUtils.ts +1 -1
  169. package/src/utils/accountUtils.ts +4 -4
  170. package/src/utils/authHelpers.ts +23 -15
  171. package/src/utils/coldBoot.ts +4 -4
  172. package/src/utils/fapiAutoDetect.ts +1 -1
  173. package/src/mixins/OxyServices.popup.ts +0 -631
  174. package/src/mixins/__tests__/popup.test.ts +0 -374
@@ -103,7 +103,9 @@
103
103
  "createAccount": "创建账户",
104
104
  "signIn": "登录",
105
105
  "verify": "验证",
106
- "resetPassword": "重置密码"
106
+ "resetPassword": "重置密码",
107
+ "signedOut": "已退出",
108
+ "close": "关闭"
107
109
  },
108
110
  "links": {
109
111
  "recoverAccount": "恢复您的账户",
@@ -115,7 +117,10 @@
115
117
  "password": "密码",
116
118
  "confirmPassword": "确认密码"
117
119
  },
118
- "revoke": "Revoke"
120
+ "revoke": "Revoke",
121
+ "errors": {
122
+ "signOutAllFailed": "退出所有账户时出现问题。请重试。"
123
+ }
119
124
  },
120
125
  "notifications": {
121
126
  "title": "Notifications",
@@ -198,5 +203,16 @@
198
203
  "revoked": "Revoked access for {{name}}",
199
204
  "revokeFailed": "Failed to revoke access"
200
205
  }
206
+ },
207
+ "accountMenu": {
208
+ "label": "账户菜单",
209
+ "manage": "管理您的 Oxy 账户",
210
+ "addAnother": "添加其他账户",
211
+ "signOutAll": "退出所有账户",
212
+ "open": "账户菜单",
213
+ "openHint": "打开账户菜单",
214
+ "openWithUser": "{{name}} 的账户菜单",
215
+ "switching": "正在切换账户…",
216
+ "signOutAccount": "退出 {{name}}"
201
217
  }
202
218
  }
@@ -303,14 +303,10 @@ export function OxyServicesAuthMixin(Base) {
303
303
  // Plant the freshly-minted tokens, mirroring `claimSessionByToken`.
304
304
  // `/auth/verify` returns the first access token (and refresh token) in
305
305
  // its body, so installing it here means callers get an authenticated
306
- // client without a second round-trip and, critically, without
307
- // falling back to the bearer-protected `GET /session/token/:sessionId`
308
- // (C1 hardening), which 401s for a brand-new identity that has no
309
- // bearer yet. `accessToken`/`refreshToken` are optional on
310
- // SessionLoginResponse; only plant when an access token is present and
311
- // default the refresh token to an empty string.
306
+ // client without a second round-trip. Refresh stays in the httpOnly
307
+ // cookie slot set by the API.
312
308
  if (res?.accessToken) {
313
- this.setTokens(res.accessToken, res.refreshToken ?? '');
309
+ this.setTokens(res.accessToken);
314
310
  }
315
311
  return res;
316
312
  }
@@ -373,24 +369,6 @@ export function OxyServicesAuthMixin(Base) {
373
369
  throw this.handleError(error);
374
370
  }
375
371
  }
376
- /**
377
- * Get access token by session ID
378
- *
379
- * SECURITY: this endpoint requires the caller to already hold a
380
- * bearer token whose user owns the referenced session (C1 hardening
381
- * in the API). For the device-flow / QR sign-in case where the
382
- * client has no bearer token yet, use `claimSessionByToken` instead.
383
- */
384
- async getTokenBySession(sessionId) {
385
- try {
386
- const res = await this.makeRequest('GET', `/session/token/${sessionId}`, undefined, { cache: false, retry: false });
387
- this.setTokens(res.accessToken);
388
- return res;
389
- }
390
- catch (error) {
391
- throw this.handleError(error);
392
- }
393
- }
394
372
  /**
395
373
  * Exchange a device-flow sessionToken for the first access token.
396
374
  *
@@ -418,7 +396,7 @@ export function OxyServicesAuthMixin(Base) {
418
396
  sessionToken,
419
397
  ...(options.deviceFingerprint ? { deviceFingerprint: options.deviceFingerprint } : {}),
420
398
  }, { cache: false, retry: false });
421
- this.setTokens(res.accessToken, res.refreshToken);
399
+ this.setTokens(res.accessToken);
422
400
  return res;
423
401
  }
424
402
  catch (error) {
@@ -430,19 +408,14 @@ export function OxyServicesAuthMixin(Base) {
430
408
  * (Google-style multi-account rebuild).
431
409
  *
432
410
  * Calls `POST {sessionBaseUrl}/auth/refresh-all` with `credentials: 'include'`
433
- * and NO bearer. The browser attaches every `oxy_rt*` cookie it has; the
434
- * server rotates each in parallel and returns one entry per VALID account.
411
+ * and NO bearer. The browser attaches every indexed `oxy_rt_${authuser}`
412
+ * cookie it has; the server rotates each in parallel and returns one entry
413
+ * per VALID account.
435
414
  *
436
415
  * Failure handling:
437
416
  * - 401 → no signed-in accounts on this device → returns `{ accounts: [] }`
438
417
  * (NOT an error; this is the cold-boot "not signed in" path).
439
- * - 404 → server is older than the multi-account endpoint. We fall back to
440
- * `POST /auth/refresh` (single-slot) and wrap its response in the
441
- * refresh-all shape so callers can treat the two paths uniformly. The
442
- * fallback entry has `authuser: 0` (the legacy slot maps to slot 0 by
443
- * convention) and a minimal `user` shape — consumers needing the full
444
- * user must fetch it separately. Always exactly one account in this
445
- * shape.
418
+ * - 404 → endpoint not deployed returns `{ accounts: [] }`.
446
419
  * - Any other non-2xx → throws via `handleError`.
447
420
  *
448
421
  * The refresh cookie itself never enters JS — only the rotated access
@@ -489,22 +462,7 @@ export function OxyServicesAuthMixin(Base) {
489
462
  return { accounts: [] };
490
463
  }
491
464
  if (response.status === 404) {
492
- // Legacy single-account refresh fallback. Wrap the response so the
493
- // caller can treat both paths identically.
494
- const legacy = await this._refreshCookieRaw();
495
- if (!legacy) {
496
- return { accounts: [] };
497
- }
498
- const fallbackAccount = {
499
- authuser: 0,
500
- accessToken: legacy.accessToken,
501
- expiresAt: legacy.expiresAt,
502
- sessionId: this._decodeSessionIdFromAccessToken(legacy.accessToken) ?? '',
503
- // Legacy /auth/refresh does NOT project the user shape; the caller
504
- // (AuthManager) is expected to hydrate via /users/me after planting.
505
- user: null,
506
- };
507
- return { accounts: [fallbackAccount] };
465
+ return { accounts: [] };
508
466
  }
509
467
  if (!response.ok) {
510
468
  throw this.handleError(new Error(`Refresh-all failed with HTTP ${response.status}`));
@@ -524,11 +482,11 @@ export function OxyServicesAuthMixin(Base) {
524
482
  if (!userId || !e.user.username) {
525
483
  continue;
526
484
  }
527
- // Normalise the legacy un-suffixed cookie (`authuser: null` on the
528
- // wire) to slot 0. The SDK surface always operates on numeric indices.
529
- const authuser = typeof e.authuser === 'number' ? e.authuser : 0;
485
+ if (typeof e.authuser !== 'number') {
486
+ continue;
487
+ }
530
488
  accounts.push({
531
- authuser,
489
+ authuser: e.authuser,
532
490
  accessToken: e.accessToken,
533
491
  expiresAt: e.expiresAt,
534
492
  sessionId: e.sessionId,
@@ -549,9 +507,8 @@ export function OxyServicesAuthMixin(Base) {
549
507
  *
550
508
  * When `authuser` is provided, the server rotates ONLY that slot
551
509
  * (`oxy_rt_${authuser}`) — sibling accounts on the same device stay
552
- * untouched. When omitted, the server picks the lowest indexed slot
553
- * present (legacy fallback applies). The refresh cookie itself never
554
- * enters JS.
510
+ * untouched. When omitted, the server picks the lowest indexed slot present.
511
+ * The refresh cookie itself never enters JS.
555
512
  *
556
513
  * Returns `null` on 401 (no cookie / expired / reused) so the caller can
557
514
  * fall through cleanly to the unauthenticated path.
@@ -606,9 +563,7 @@ export function OxyServicesAuthMixin(Base) {
606
563
  }
607
564
  /**
608
565
  * Internal: raw `POST /auth/refresh[?authuser=N]` call returning the
609
- * minted access token. Returns `null` on 401 / non-2xx. Used as both the
610
- * implementation of `refreshTokenViaCookie` and the legacy fallback for
611
- * `refreshAllSessions` against older servers.
566
+ * minted access token. Returns `null` on 401 / non-2xx.
612
567
  *
613
568
  * @internal
614
569
  */
@@ -636,11 +591,13 @@ export function OxyServicesAuthMixin(Base) {
636
591
  return null;
637
592
  }
638
593
  const expiresAt = typeof payload.expiresAt === 'string' ? payload.expiresAt : '';
639
- const respAuthuser = typeof payload.authuser === 'number' ? payload.authuser : null;
594
+ if (typeof payload.authuser !== 'number') {
595
+ return null;
596
+ }
640
597
  return {
641
598
  accessToken: payload.accessToken,
642
599
  expiresAt,
643
- authuser: respAuthuser,
600
+ authuser: payload.authuser,
644
601
  };
645
602
  }
646
603
  /**
@@ -84,7 +84,7 @@ let fedCMActiveController = null;
84
84
  * - Firefox: Not yet supported (fallback required)
85
85
  *
86
86
  * Key Features:
87
- * - No redirects or popups required
87
+ * - No redirects or secondary windows required
88
88
  * - Browser-native UI prompts
89
89
  * - Privacy-preserving (IdP can't track users)
90
90
  * - Automatic SSO across domains
@@ -125,7 +125,7 @@ export function OxyServicesFedCMMixin(Base) {
125
125
  *
126
126
  * This provides a Google-style authentication experience:
127
127
  * - Browser shows native "Sign in with Oxy" prompt
128
- * - No redirect or popup required
128
+ * - No redirect or secondary window required
129
129
  * - User approves → credential exchange happens in browser
130
130
  * - All apps automatically get SSO after first sign-in
131
131
  *
@@ -139,8 +139,8 @@ export function OxyServicesFedCMMixin(Base) {
139
139
  * const session = await oxyServices.signInWithFedCM();
140
140
  * console.log('Signed in:', session.user);
141
141
  * } catch (error) {
142
- * // Fallback to popup or redirect auth
143
- * await oxyServices.signInWithPopup();
142
+ * // Fallback to redirect auth
143
+ * oxyServices.signInWithRedirect();
144
144
  * }
145
145
  * ```
146
146
  */
@@ -205,11 +205,10 @@ export function OxyServicesFedCMMixin(Base) {
205
205
  debug.log('Interactive sign-in: Got credential, exchanging for session');
206
206
  // Exchange FedCM ID token for Oxy session
207
207
  const session = await this.exchangeIdTokenForSession(credential.token);
208
- // Store access token in HttpService. `accessToken`/`refreshToken` are
209
- // declared optional on SessionLoginResponse; default the refresh token to
210
- // an empty string when the exchange did not return one.
208
+ // Store the access token in HttpService. Refresh stays in the httpOnly
209
+ // cookie slot set by the API.
211
210
  if (session?.accessToken) {
212
- this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
211
+ this.httpService.setTokens(session.accessToken);
213
212
  }
214
213
  // Store the user ID as loginHint for future FedCM requests — only now, after
215
214
  // a real successful exchange, so we never persist a hint that cannot resolve.
@@ -360,11 +359,10 @@ export function OxyServicesFedCMMixin(Base) {
360
359
  debug.error('Silent SSO: Exchange returned session without user:', session);
361
360
  return null;
362
361
  }
363
- // Set the access token. `accessToken`/`refreshToken` are declared optional
364
- // on SessionLoginResponse; default the refresh token to an empty string when
365
- // the exchange did not return one.
362
+ // Store the access token. Refresh stays in the httpOnly cookie slot set by
363
+ // the API.
366
364
  if (session.accessToken) {
367
- this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
365
+ this.httpService.setTokens(session.accessToken);
368
366
  debug.log('Silent SSO: Access token set');
369
367
  }
370
368
  else {