@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
  }
@@ -308,14 +308,10 @@ function OxyServicesAuthMixin(Base) {
308
308
  // Plant the freshly-minted tokens, mirroring `claimSessionByToken`.
309
309
  // `/auth/verify` returns the first access token (and refresh token) in
310
310
  // its body, so installing it here means callers get an authenticated
311
- // client without a second round-trip and, critically, without
312
- // falling back to the bearer-protected `GET /session/token/:sessionId`
313
- // (C1 hardening), which 401s for a brand-new identity that has no
314
- // bearer yet. `accessToken`/`refreshToken` are optional on
315
- // SessionLoginResponse; only plant when an access token is present and
316
- // default the refresh token to an empty string.
311
+ // client without a second round-trip. Refresh stays in the httpOnly
312
+ // cookie slot set by the API.
317
313
  if (res?.accessToken) {
318
- this.setTokens(res.accessToken, res.refreshToken ?? '');
314
+ this.setTokens(res.accessToken);
319
315
  }
320
316
  return res;
321
317
  }
@@ -378,24 +374,6 @@ function OxyServicesAuthMixin(Base) {
378
374
  throw this.handleError(error);
379
375
  }
380
376
  }
381
- /**
382
- * Get access token by session ID
383
- *
384
- * SECURITY: this endpoint requires the caller to already hold a
385
- * bearer token whose user owns the referenced session (C1 hardening
386
- * in the API). For the device-flow / QR sign-in case where the
387
- * client has no bearer token yet, use `claimSessionByToken` instead.
388
- */
389
- async getTokenBySession(sessionId) {
390
- try {
391
- const res = await this.makeRequest('GET', `/session/token/${sessionId}`, undefined, { cache: false, retry: false });
392
- this.setTokens(res.accessToken);
393
- return res;
394
- }
395
- catch (error) {
396
- throw this.handleError(error);
397
- }
398
- }
399
377
  /**
400
378
  * Exchange a device-flow sessionToken for the first access token.
401
379
  *
@@ -423,7 +401,7 @@ function OxyServicesAuthMixin(Base) {
423
401
  sessionToken,
424
402
  ...(options.deviceFingerprint ? { deviceFingerprint: options.deviceFingerprint } : {}),
425
403
  }, { cache: false, retry: false });
426
- this.setTokens(res.accessToken, res.refreshToken);
404
+ this.setTokens(res.accessToken);
427
405
  return res;
428
406
  }
429
407
  catch (error) {
@@ -435,19 +413,14 @@ function OxyServicesAuthMixin(Base) {
435
413
  * (Google-style multi-account rebuild).
436
414
  *
437
415
  * Calls `POST {sessionBaseUrl}/auth/refresh-all` with `credentials: 'include'`
438
- * and NO bearer. The browser attaches every `oxy_rt*` cookie it has; the
439
- * server rotates each in parallel and returns one entry per VALID account.
416
+ * and NO bearer. The browser attaches every indexed `oxy_rt_${authuser}`
417
+ * cookie it has; the server rotates each in parallel and returns one entry
418
+ * per VALID account.
440
419
  *
441
420
  * Failure handling:
442
421
  * - 401 → no signed-in accounts on this device → returns `{ accounts: [] }`
443
422
  * (NOT an error; this is the cold-boot "not signed in" path).
444
- * - 404 → server is older than the multi-account endpoint. We fall back to
445
- * `POST /auth/refresh` (single-slot) and wrap its response in the
446
- * refresh-all shape so callers can treat the two paths uniformly. The
447
- * fallback entry has `authuser: 0` (the legacy slot maps to slot 0 by
448
- * convention) and a minimal `user` shape — consumers needing the full
449
- * user must fetch it separately. Always exactly one account in this
450
- * shape.
423
+ * - 404 → endpoint not deployed returns `{ accounts: [] }`.
451
424
  * - Any other non-2xx → throws via `handleError`.
452
425
  *
453
426
  * The refresh cookie itself never enters JS — only the rotated access
@@ -494,22 +467,7 @@ function OxyServicesAuthMixin(Base) {
494
467
  return { accounts: [] };
495
468
  }
496
469
  if (response.status === 404) {
497
- // Legacy single-account refresh fallback. Wrap the response so the
498
- // caller can treat both paths identically.
499
- const legacy = await this._refreshCookieRaw();
500
- if (!legacy) {
501
- return { accounts: [] };
502
- }
503
- const fallbackAccount = {
504
- authuser: 0,
505
- accessToken: legacy.accessToken,
506
- expiresAt: legacy.expiresAt,
507
- sessionId: this._decodeSessionIdFromAccessToken(legacy.accessToken) ?? '',
508
- // Legacy /auth/refresh does NOT project the user shape; the caller
509
- // (AuthManager) is expected to hydrate via /users/me after planting.
510
- user: null,
511
- };
512
- return { accounts: [fallbackAccount] };
470
+ return { accounts: [] };
513
471
  }
514
472
  if (!response.ok) {
515
473
  throw this.handleError(new Error(`Refresh-all failed with HTTP ${response.status}`));
@@ -529,11 +487,11 @@ function OxyServicesAuthMixin(Base) {
529
487
  if (!userId || !e.user.username) {
530
488
  continue;
531
489
  }
532
- // Normalise the legacy un-suffixed cookie (`authuser: null` on the
533
- // wire) to slot 0. The SDK surface always operates on numeric indices.
534
- const authuser = typeof e.authuser === 'number' ? e.authuser : 0;
490
+ if (typeof e.authuser !== 'number') {
491
+ continue;
492
+ }
535
493
  accounts.push({
536
- authuser,
494
+ authuser: e.authuser,
537
495
  accessToken: e.accessToken,
538
496
  expiresAt: e.expiresAt,
539
497
  sessionId: e.sessionId,
@@ -554,9 +512,8 @@ function OxyServicesAuthMixin(Base) {
554
512
  *
555
513
  * When `authuser` is provided, the server rotates ONLY that slot
556
514
  * (`oxy_rt_${authuser}`) — sibling accounts on the same device stay
557
- * untouched. When omitted, the server picks the lowest indexed slot
558
- * present (legacy fallback applies). The refresh cookie itself never
559
- * enters JS.
515
+ * untouched. When omitted, the server picks the lowest indexed slot present.
516
+ * The refresh cookie itself never enters JS.
560
517
  *
561
518
  * Returns `null` on 401 (no cookie / expired / reused) so the caller can
562
519
  * fall through cleanly to the unauthenticated path.
@@ -611,9 +568,7 @@ function OxyServicesAuthMixin(Base) {
611
568
  }
612
569
  /**
613
570
  * Internal: raw `POST /auth/refresh[?authuser=N]` call returning the
614
- * minted access token. Returns `null` on 401 / non-2xx. Used as both the
615
- * implementation of `refreshTokenViaCookie` and the legacy fallback for
616
- * `refreshAllSessions` against older servers.
571
+ * minted access token. Returns `null` on 401 / non-2xx.
617
572
  *
618
573
  * @internal
619
574
  */
@@ -641,11 +596,13 @@ function OxyServicesAuthMixin(Base) {
641
596
  return null;
642
597
  }
643
598
  const expiresAt = typeof payload.expiresAt === 'string' ? payload.expiresAt : '';
644
- const respAuthuser = typeof payload.authuser === 'number' ? payload.authuser : null;
599
+ if (typeof payload.authuser !== 'number') {
600
+ return null;
601
+ }
645
602
  return {
646
603
  accessToken: payload.accessToken,
647
604
  expiresAt,
648
- authuser: respAuthuser,
605
+ authuser: payload.authuser,
649
606
  };
650
607
  }
651
608
  /**
@@ -88,7 +88,7 @@ let fedCMActiveController = null;
88
88
  * - Firefox: Not yet supported (fallback required)
89
89
  *
90
90
  * Key Features:
91
- * - No redirects or popups required
91
+ * - No redirects or secondary windows required
92
92
  * - Browser-native UI prompts
93
93
  * - Privacy-preserving (IdP can't track users)
94
94
  * - Automatic SSO across domains
@@ -129,7 +129,7 @@ function OxyServicesFedCMMixin(Base) {
129
129
  *
130
130
  * This provides a Google-style authentication experience:
131
131
  * - Browser shows native "Sign in with Oxy" prompt
132
- * - No redirect or popup required
132
+ * - No redirect or secondary window required
133
133
  * - User approves → credential exchange happens in browser
134
134
  * - All apps automatically get SSO after first sign-in
135
135
  *
@@ -143,8 +143,8 @@ function OxyServicesFedCMMixin(Base) {
143
143
  * const session = await oxyServices.signInWithFedCM();
144
144
  * console.log('Signed in:', session.user);
145
145
  * } catch (error) {
146
- * // Fallback to popup or redirect auth
147
- * await oxyServices.signInWithPopup();
146
+ * // Fallback to redirect auth
147
+ * oxyServices.signInWithRedirect();
148
148
  * }
149
149
  * ```
150
150
  */
@@ -209,11 +209,10 @@ function OxyServicesFedCMMixin(Base) {
209
209
  debug.log('Interactive sign-in: Got credential, exchanging for session');
210
210
  // Exchange FedCM ID token for Oxy session
211
211
  const session = await this.exchangeIdTokenForSession(credential.token);
212
- // Store access token in HttpService. `accessToken`/`refreshToken` are
213
- // declared optional on SessionLoginResponse; default the refresh token to
214
- // an empty string when the exchange did not return one.
212
+ // Store the access token in HttpService. Refresh stays in the httpOnly
213
+ // cookie slot set by the API.
215
214
  if (session?.accessToken) {
216
- this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
215
+ this.httpService.setTokens(session.accessToken);
217
216
  }
218
217
  // Store the user ID as loginHint for future FedCM requests — only now, after
219
218
  // a real successful exchange, so we never persist a hint that cannot resolve.
@@ -364,11 +363,10 @@ function OxyServicesFedCMMixin(Base) {
364
363
  debug.error('Silent SSO: Exchange returned session without user:', session);
365
364
  return null;
366
365
  }
367
- // Set the access token. `accessToken`/`refreshToken` are declared optional
368
- // on SessionLoginResponse; default the refresh token to an empty string when
369
- // the exchange did not return one.
366
+ // Store the access token. Refresh stays in the httpOnly cookie slot set by
367
+ // the API.
370
368
  if (session.accessToken) {
371
- this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
369
+ this.httpService.setTokens(session.accessToken);
372
370
  debug.log('Silent SSO: Access token set');
373
371
  }
374
372
  else {