@oxyhq/core 12.10.6 → 12.11.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 (126) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/boot/sessionColdBoot.js +142 -37
  3. package/dist/cjs/i18n/locales/ar-SA.json +13 -1
  4. package/dist/cjs/i18n/locales/ca-ES.json +13 -1
  5. package/dist/cjs/i18n/locales/de-DE.json +13 -1
  6. package/dist/cjs/i18n/locales/en-US.json +13 -4
  7. package/dist/cjs/i18n/locales/es-ES.json +13 -4
  8. package/dist/cjs/i18n/locales/fr-FR.json +13 -1
  9. package/dist/cjs/i18n/locales/it-IT.json +13 -1
  10. package/dist/cjs/i18n/locales/ja-JP.json +13 -1
  11. package/dist/cjs/i18n/locales/ko-KR.json +13 -1
  12. package/dist/cjs/i18n/locales/locales/ar-SA.json +13 -1
  13. package/dist/cjs/i18n/locales/locales/ca-ES.json +13 -1
  14. package/dist/cjs/i18n/locales/locales/de-DE.json +13 -1
  15. package/dist/cjs/i18n/locales/locales/en-US.json +13 -4
  16. package/dist/cjs/i18n/locales/locales/es-ES.json +13 -4
  17. package/dist/cjs/i18n/locales/locales/fr-FR.json +13 -1
  18. package/dist/cjs/i18n/locales/locales/it-IT.json +13 -1
  19. package/dist/cjs/i18n/locales/locales/ja-JP.json +13 -1
  20. package/dist/cjs/i18n/locales/locales/ko-KR.json +13 -1
  21. package/dist/cjs/i18n/locales/locales/pt-PT.json +13 -1
  22. package/dist/cjs/i18n/locales/locales/zh-CN.json +13 -1
  23. package/dist/cjs/i18n/locales/pt-PT.json +13 -1
  24. package/dist/cjs/i18n/locales/zh-CN.json +13 -1
  25. package/dist/cjs/index.js +31 -5
  26. package/dist/cjs/mixins/OxyServices.auth.js +257 -17
  27. package/dist/cjs/mixins/OxyServices.deviceBoot.js +54 -3
  28. package/dist/cjs/mixins/OxyServices.notifications.js +63 -0
  29. package/dist/cjs/mixins/index.js +5 -0
  30. package/dist/cjs/session/SessionClient.js +40 -4
  31. package/dist/cjs/session/accountDialogController.js +288 -41
  32. package/dist/cjs/session/identityPin.js +256 -0
  33. package/dist/cjs/session/identitySession.js +135 -0
  34. package/dist/cjs/session/projectSessionState.js +54 -15
  35. package/dist/cjs/session/refresh.js +73 -8
  36. package/dist/cjs/utils/commonsDelivery.js +59 -0
  37. package/dist/cjs/utils/oauthPkce.js +33 -3
  38. package/dist/esm/.tsbuildinfo +1 -1
  39. package/dist/esm/boot/sessionColdBoot.js +142 -37
  40. package/dist/esm/i18n/locales/ar-SA.json +13 -1
  41. package/dist/esm/i18n/locales/ca-ES.json +13 -1
  42. package/dist/esm/i18n/locales/de-DE.json +13 -1
  43. package/dist/esm/i18n/locales/en-US.json +13 -4
  44. package/dist/esm/i18n/locales/es-ES.json +13 -4
  45. package/dist/esm/i18n/locales/fr-FR.json +13 -1
  46. package/dist/esm/i18n/locales/it-IT.json +13 -1
  47. package/dist/esm/i18n/locales/ja-JP.json +13 -1
  48. package/dist/esm/i18n/locales/ko-KR.json +13 -1
  49. package/dist/esm/i18n/locales/locales/ar-SA.json +13 -1
  50. package/dist/esm/i18n/locales/locales/ca-ES.json +13 -1
  51. package/dist/esm/i18n/locales/locales/de-DE.json +13 -1
  52. package/dist/esm/i18n/locales/locales/en-US.json +13 -4
  53. package/dist/esm/i18n/locales/locales/es-ES.json +13 -4
  54. package/dist/esm/i18n/locales/locales/fr-FR.json +13 -1
  55. package/dist/esm/i18n/locales/locales/it-IT.json +13 -1
  56. package/dist/esm/i18n/locales/locales/ja-JP.json +13 -1
  57. package/dist/esm/i18n/locales/locales/ko-KR.json +13 -1
  58. package/dist/esm/i18n/locales/locales/pt-PT.json +13 -1
  59. package/dist/esm/i18n/locales/locales/zh-CN.json +13 -1
  60. package/dist/esm/i18n/locales/pt-PT.json +13 -1
  61. package/dist/esm/i18n/locales/zh-CN.json +13 -1
  62. package/dist/esm/index.js +14 -1
  63. package/dist/esm/mixins/OxyServices.auth.js +257 -17
  64. package/dist/esm/mixins/OxyServices.deviceBoot.js +52 -3
  65. package/dist/esm/mixins/OxyServices.notifications.js +60 -0
  66. package/dist/esm/mixins/index.js +5 -0
  67. package/dist/esm/session/SessionClient.js +40 -4
  68. package/dist/esm/session/accountDialogController.js +288 -41
  69. package/dist/esm/session/identityPin.js +249 -0
  70. package/dist/esm/session/identitySession.js +131 -0
  71. package/dist/esm/session/projectSessionState.js +54 -15
  72. package/dist/esm/session/refresh.js +73 -8
  73. package/dist/esm/utils/commonsDelivery.js +54 -0
  74. package/dist/esm/utils/oauthPkce.js +31 -2
  75. package/dist/types/.tsbuildinfo +1 -1
  76. package/dist/types/boot/sessionColdBoot.d.ts +38 -1
  77. package/dist/types/index.d.ts +12 -4
  78. package/dist/types/mixins/OxyServices.auth.d.ts +262 -11
  79. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +31 -1
  80. package/dist/types/mixins/OxyServices.notifications.d.ts +143 -0
  81. package/dist/types/mixins/index.d.ts +2 -1
  82. package/dist/types/session/SessionClient.d.ts +28 -0
  83. package/dist/types/session/accountDialogController.d.ts +187 -9
  84. package/dist/types/session/identityPin.d.ts +87 -0
  85. package/dist/types/session/identitySession.d.ts +105 -0
  86. package/dist/types/session/projectSessionState.d.ts +21 -19
  87. package/dist/types/session/refresh.d.ts +32 -1
  88. package/dist/types/utils/commonsDelivery.d.ts +105 -0
  89. package/dist/types/utils/oauthPkce.d.ts +21 -1
  90. package/package.json +2 -2
  91. package/src/boot/__tests__/sessionColdBoot.identity.test.ts +393 -0
  92. package/src/boot/sessionColdBoot.ts +183 -37
  93. package/src/i18n/locales/ar-SA.json +13 -1
  94. package/src/i18n/locales/ca-ES.json +13 -1
  95. package/src/i18n/locales/de-DE.json +13 -1
  96. package/src/i18n/locales/en-US.json +13 -4
  97. package/src/i18n/locales/es-ES.json +13 -4
  98. package/src/i18n/locales/fr-FR.json +13 -1
  99. package/src/i18n/locales/it-IT.json +13 -1
  100. package/src/i18n/locales/ja-JP.json +13 -1
  101. package/src/i18n/locales/ko-KR.json +13 -1
  102. package/src/i18n/locales/pt-PT.json +13 -1
  103. package/src/i18n/locales/zh-CN.json +13 -1
  104. package/src/index.ts +49 -0
  105. package/src/mixins/OxyServices.auth.ts +464 -24
  106. package/src/mixins/OxyServices.deviceBoot.ts +54 -2
  107. package/src/mixins/OxyServices.notifications.ts +142 -0
  108. package/src/mixins/__tests__/commonsSignIn.test.ts +754 -2
  109. package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +113 -0
  110. package/src/mixins/__tests__/preSessionSkipAuth.test.ts +18 -0
  111. package/src/mixins/index.ts +6 -0
  112. package/src/session/SessionClient.ts +57 -4
  113. package/src/session/__tests__/SessionClient.identityPin.test.ts +175 -0
  114. package/src/session/__tests__/accountDialogController.test.ts +812 -1
  115. package/src/session/__tests__/identityPin.test.ts +131 -0
  116. package/src/session/__tests__/identitySession.test.ts +194 -0
  117. package/src/session/__tests__/projectSessionState.test.ts +88 -0
  118. package/src/session/__tests__/refresh.identityPin.test.ts +306 -0
  119. package/src/session/accountDialogController.ts +435 -44
  120. package/src/session/identityPin.ts +310 -0
  121. package/src/session/identitySession.ts +206 -0
  122. package/src/session/projectSessionState.ts +61 -13
  123. package/src/session/refresh.ts +103 -9
  124. package/src/utils/__tests__/oauthPkce.test.ts +51 -0
  125. package/src/utils/commonsDelivery.ts +132 -0
  126. package/src/utils/oauthPkce.ts +56 -3
@@ -0,0 +1,54 @@
1
+ import { getPlatformOS } from './platform.js';
2
+ /**
3
+ * Choose the ONE primary delivery route for a "Sign in with Oxy" request.
4
+ *
5
+ * Decision order (issue #691, "Automatic delivery selection"):
6
+ *
7
+ * 1. Mobile with a verified Commons link available → open Commons directly.
8
+ * No push is needed when the identity is already reachable on this device.
9
+ * 2. At least one eligible Commons installation was pushed to → await the push.
10
+ * 3. Otherwise → QR.
11
+ *
12
+ * A caller that already knows it is on step 1 does not need to call
13
+ * `deliverCommonsSignIn` at all; every other caller runs the delivery
14
+ * round-trip first and passes its `targets` in here.
15
+ *
16
+ * `pushTargets` is server-derived, so it is validated rather than trusted: a
17
+ * negative, fractional, non-finite, or otherwise malformed count is treated as
18
+ * "no targets" and degrades to QR instead of parking the user on a "check your
19
+ * phone" screen that nothing will ever wake.
20
+ */
21
+ export function selectCommonsDelivery(facts) {
22
+ if (facts.platform === 'mobile' && facts.commonsAvailable) {
23
+ return 'open-commons';
24
+ }
25
+ if (Number.isInteger(facts.pushTargets) && facts.pushTargets >= 1) {
26
+ return 'await-push';
27
+ }
28
+ return 'qr';
29
+ }
30
+ /**
31
+ * Map a `deliverCommonsSignIn` result onto the `pushTargets` fact for
32
+ * {@link selectCommonsDelivery}. The server reports eligible install *count*
33
+ * separately from whether any push was accepted — a transport failure can leave
34
+ * `delivered: false` with `targets > 0`, which must NOT park the user on
35
+ * "check your phone".
36
+ */
37
+ export function pushTargetsFromDelivery(result) {
38
+ return result.delivered ? result.targets : 0;
39
+ }
40
+ /**
41
+ * Classify the current runtime surface for delivery selection. Callers that
42
+ * cannot confidently detect the platform should not use this — pass
43
+ * `'unknown'` explicitly instead.
44
+ */
45
+ export function commonsDeliveryPlatform() {
46
+ const os = getPlatformOS();
47
+ if (os === 'ios' || os === 'android') {
48
+ return 'mobile';
49
+ }
50
+ if (os === 'web') {
51
+ return 'desktop';
52
+ }
53
+ return 'unknown';
54
+ }
@@ -134,12 +134,17 @@ export function buildOAuthAuthorizeUrl(params) {
134
134
  if (params.prompt) {
135
135
  url.searchParams.set('prompt', params.prompt);
136
136
  }
137
+ if (params.responseMode) {
138
+ url.searchParams.set('response_mode', params.responseMode);
139
+ }
137
140
  return url.toString();
138
141
  }
139
142
  /** `sessionStorage` key for the OAuth CSRF `state` across an authorize redirect. */
140
143
  export const OXY_OAUTH_STATE_STORAGE_KEY = 'oxy_oauth_state';
141
144
  /** `sessionStorage` key for the PKCE `code_verifier` across an authorize redirect. */
142
145
  export const OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = 'oxy_oauth_code_verifier';
146
+ /** `sessionStorage` key — the exact `redirect_uri` sent on the authorize request. */
147
+ export const OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = 'oxy.oauth_redirect_uri';
143
148
  /** `sessionStorage` key — at most one silent OAuth attempt per navigation. */
144
149
  export const OXY_SILENT_OAUTH_ATTEMPTED_KEY = 'oxy.silent_oauth_attempted';
145
150
  /** `sessionStorage` key — blocks further cross-origin auto-restore in this tab. */
@@ -166,6 +171,25 @@ export function normalizeOAuthRedirectUri(input) {
166
171
  return input;
167
172
  }
168
173
  }
174
+ /**
175
+ * Collapse `https://app.example/` → `https://app.example` for OAuth binding.
176
+ * Path-qualified redirect URIs are preserved — matches the API token exchange.
177
+ */
178
+ export function canonicalizeOAuthRedirectUri(redirectUri) {
179
+ try {
180
+ const parsed = new URL(redirectUri);
181
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
182
+ return redirectUri;
183
+ }
184
+ if (parsed.pathname === '/' && !parsed.search && !parsed.hash) {
185
+ return parsed.origin;
186
+ }
187
+ return redirectUri;
188
+ }
189
+ catch {
190
+ return redirectUri;
191
+ }
192
+ }
169
193
  /**
170
194
  * Is this a safe same-origin path to restore into the address bar?
171
195
  *
@@ -231,13 +255,16 @@ export function consumeOAuthReturnPath() {
231
255
  }
232
256
  }
233
257
  /** Persist the OAuth handshake for a full-page redirect return (web only). */
234
- export function persistOAuthHandshake(state, codeVerifier) {
258
+ export function persistOAuthHandshake(state, codeVerifier, redirectUri) {
235
259
  const store = globalThis.sessionStorage;
236
260
  try {
237
261
  if (!store)
238
262
  throw new Error('sessionStorage is unavailable');
239
263
  store.setItem(OXY_OAUTH_STATE_STORAGE_KEY, state);
240
264
  store.setItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, codeVerifier);
265
+ if (redirectUri) {
266
+ store.setItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY, canonicalizeOAuthRedirectUri(redirectUri));
267
+ }
241
268
  return true;
242
269
  }
243
270
  catch (error) {
@@ -254,7 +281,8 @@ export function readOAuthHandshake() {
254
281
  const codeVerifier = store.getItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
255
282
  if (!state || !codeVerifier)
256
283
  return null;
257
- return { state, codeVerifier };
284
+ const redirectUri = store.getItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY) ?? undefined;
285
+ return redirectUri ? { state, codeVerifier, redirectUri } : { state, codeVerifier };
258
286
  }
259
287
  /** Drop persisted OAuth handshake keys after a successful or aborted return. */
260
288
  export function clearOAuthHandshake() {
@@ -262,6 +290,7 @@ export function clearOAuthHandshake() {
262
290
  try {
263
291
  store?.removeItem(OXY_OAUTH_STATE_STORAGE_KEY);
264
292
  store?.removeItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
293
+ store?.removeItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY);
265
294
  // The return path belongs to this handshake. Leaving it behind would let a
266
295
  // later, unrelated navigation in the same tab be redirected by it.
267
296
  store?.removeItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);