@oxyhq/core 12.11.1 → 13.0.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 (41) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/index.js +2 -12
  3. package/dist/cjs/mixins/OxyServices.deviceBoot.js +0 -31
  4. package/dist/cjs/server/index.js +1 -6
  5. package/dist/cjs/server/rateLimit.js +3 -0
  6. package/dist/cjs/session/accountDialogController.js +6 -8
  7. package/dist/cjs/utils/oauthPkce.js +1 -5
  8. package/dist/cjs/utils/officialOrigins.js +3 -73
  9. package/dist/esm/.tsbuildinfo +1 -1
  10. package/dist/esm/index.js +2 -3
  11. package/dist/esm/mixins/OxyServices.deviceBoot.js +1 -32
  12. package/dist/esm/server/index.js +1 -1
  13. package/dist/esm/server/rateLimit.js +3 -0
  14. package/dist/esm/session/accountDialogController.js +6 -8
  15. package/dist/esm/utils/oauthPkce.js +0 -4
  16. package/dist/esm/utils/officialOrigins.js +3 -68
  17. package/dist/types/.tsbuildinfo +1 -1
  18. package/dist/types/index.d.ts +3 -5
  19. package/dist/types/mixins/OxyServices.auth.d.ts +1 -12
  20. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +1 -5
  21. package/dist/types/server/index.d.ts +1 -1
  22. package/dist/types/session/accountDialogController.d.ts +9 -15
  23. package/dist/types/utils/oauthPkce.d.ts +11 -7
  24. package/dist/types/utils/officialOrigins.d.ts +3 -13
  25. package/package.json +3 -3
  26. package/src/index.ts +6 -14
  27. package/src/mixins/OxyServices.auth.ts +6 -13
  28. package/src/mixins/OxyServices.deviceBoot.ts +0 -47
  29. package/src/mixins/__tests__/commonsSignIn.test.ts +9 -2
  30. package/src/server/index.ts +1 -8
  31. package/src/server/rateLimit.ts +3 -0
  32. package/src/session/__tests__/accountDialogController.test.ts +3 -5
  33. package/src/session/accountDialogController.ts +12 -18
  34. package/src/utils/__tests__/officialOrigins.test.ts +0 -57
  35. package/src/utils/oauthPkce.ts +11 -9
  36. package/src/utils/officialOrigins.ts +3 -70
  37. package/dist/cjs/session/hubSync.js +0 -55
  38. package/dist/esm/session/hubSync.js +0 -51
  39. package/dist/types/session/hubSync.d.ts +0 -20
  40. package/src/session/__tests__/hubSync.test.ts +0 -51
  41. package/src/session/hubSync.ts +0 -79
package/dist/esm/index.js CHANGED
@@ -148,9 +148,8 @@ export { runColdBoot } from './utils/coldBoot.js';
148
148
  // OAuth 2.0 Authorization Code + PKCE helpers ("Sign in with Oxy" third party).
149
149
  // Standard OAuth against auth.oxy.so/authorize — no FedCM/cookies/SSO bounce.
150
150
  // ---------------------------------------------------------------------------
151
- export { buildOAuthAuthorizeUrl, computeCodeChallenge, generateOAuthState, generatePkcePair, DEFAULT_OAUTH_SCOPE, OXY_AUTHORIZE_URL, OXY_OAUTH_STATE_STORAGE_KEY, OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, OXY_OAUTH_REDIRECT_URI_STORAGE_KEY, OXY_SILENT_OAUTH_ATTEMPTED_KEY, OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY, OXY_OAUTH_RETURN_PATH_STORAGE_KEY, normalizeOAuthRedirectUri, canonicalizeOAuthRedirectUri, persistOAuthHandshake, readOAuthHandshake, clearOAuthHandshake, persistOAuthReturnPath, consumeOAuthReturnPath, } from './utils/oauthPkce.js';
152
- export { buildIdpHubOrigin, buildHubSyncUrl, isIdpHubOrigin, isLoopbackOrigin, isOfficialWebOrigin, isAllowedDeviceJoinOrigin, normalizeOfficialReturnOrigin, parseHubSyncReturnUrl, } from './utils/officialOrigins.js';
153
- export { syncHubAfterSignIn, redeemHubTicketOnHub, } from './session/hubSync.js';
151
+ export { buildOAuthAuthorizeUrl, computeCodeChallenge, generateOAuthState, generatePkcePair, DEFAULT_OAUTH_SCOPE, OXY_AUTHORIZE_URL, OXY_OAUTH_STATE_STORAGE_KEY, OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, OXY_OAUTH_REDIRECT_URI_STORAGE_KEY, OXY_OAUTH_RETURN_PATH_STORAGE_KEY, normalizeOAuthRedirectUri, canonicalizeOAuthRedirectUri, persistOAuthHandshake, readOAuthHandshake, clearOAuthHandshake, persistOAuthReturnPath, consumeOAuthReturnPath, } from './utils/oauthPkce.js';
152
+ export { isLoopbackOrigin, isOfficialWebOrigin, isAllowedDeviceJoinOrigin, } from './utils/officialOrigins.js';
154
153
  // ---------------------------------------------------------------------------
155
154
  // Session sync (device-scoped multi-account session client)
156
155
  // ---------------------------------------------------------------------------
@@ -13,7 +13,7 @@
13
13
  * the cold boot / re-mint handler own persistence and `setTokens`, so the same
14
14
  * primitive can be reused from either without double-planting.
15
15
  */
16
- import { deviceTokenMintResponseSchema, deviceHubTicketIssueResponseSchema, deviceHubTicketRedeemResponseSchema, safeParseContract, } from '@oxyhq/contracts';
16
+ import { deviceTokenMintResponseSchema, safeParseContract, } from '@oxyhq/contracts';
17
17
  /**
18
18
  * The server's `401 account_not_on_device` for a PINNED mint: the requested
19
19
  * `accountId` is not (or is no longer) a live account of this device session.
@@ -105,36 +105,5 @@ export function OxyServicesDeviceBootMixin(Base) {
105
105
  throw normalized;
106
106
  }
107
107
  }
108
- /** Mint a one-time hub sync ticket (bearer required). */
109
- async issueHubTicket(returnOrigin) {
110
- try {
111
- const res = await this.makeRequest('POST', '/session/device/hub-ticket', { returnOrigin }, { cache: false });
112
- const parsed = safeParseContract(deviceHubTicketIssueResponseSchema, res);
113
- if (!parsed) {
114
- throw new Error('session/device/hub-ticket returned an unexpected response shape');
115
- }
116
- return parsed;
117
- }
118
- catch (error) {
119
- throw this.handleError(error);
120
- }
121
- }
122
- /** Redeem a hub sync ticket for a fresh device secret (public). */
123
- async redeemHubTicket(ticket, returnOrigin) {
124
- try {
125
- const res = await this.makeRequest('POST', '/session/device/redeem-ticket', { ticket, returnOrigin },
126
- // Public device-hub sync mint (bearer-less). Same control-plane class as
127
- // the device-secret mint — bypassQueue so it never waits for a slot.
128
- { cache: false, skipAuth: true, bypassQueue: true });
129
- const parsed = safeParseContract(deviceHubTicketRedeemResponseSchema, res);
130
- if (!parsed) {
131
- throw new Error('session/device/redeem-ticket returned an unexpected response shape');
132
- }
133
- return parsed;
134
- }
135
- catch (error) {
136
- throw this.handleError(error);
137
- }
138
- }
139
108
  };
140
109
  }
@@ -27,4 +27,4 @@ export { verifySecret } from './verifySecret.js';
27
27
  // Pure host handling (no browser deps), so it is safe on the server subpath and
28
28
  // lets `@oxyhq/api` derive `auth.<apex>` without duplicating PSL logic.
29
29
  export { registrableApex } from '../utils/registrableApex.js';
30
- export { buildIdpHubOrigin, buildHubSyncUrl, isIdpHubOrigin, isOfficialWebOrigin, normalizeOfficialReturnOrigin, parseHubSyncReturnUrl, } from '../utils/officialOrigins.js';
30
+ export { isOfficialWebOrigin } from '../utils/officialOrigins.js';
@@ -168,6 +168,9 @@ export function createOxyRateLimit(oxy, options = {}) {
168
168
  standardHeaders: true,
169
169
  legacyHeaders: false,
170
170
  skip,
171
+ // hashAnonymousIp already buckets IPv6 to /56 before HMAC; disable the v8
172
+ // static source scan that false-positives on req.ip (ERR_ERL_KEY_GEN_IPV6).
173
+ validate: { keyGeneratorIpFallback: false },
171
174
  });
172
175
  return (req, res, next) => {
173
176
  // Skipped paths bypass BOTH session resolution and limiting — cheap and
@@ -477,9 +477,9 @@ export class AccountDialogController {
477
477
  user: result.user,
478
478
  accessToken: result.accessToken,
479
479
  }, result.user,
480
- // A switch is IN-PLACE: commit without the hub-sync redirect (the
481
- // device is already known/synced). Cross-tab/app propagation rides the
482
- // server's `session_state` socket broadcast, not a navigation.
480
+ // A switch is IN-PLACE: use the switch commit funnel (not sign-in).
481
+ // Cross-tab/app propagation rides the server's `session_state` socket
482
+ // broadcast, not a navigation.
483
483
  { fromSwitch: true });
484
484
  }
485
485
  // Re-project + refetch immediately; the subscription also fires.
@@ -982,11 +982,9 @@ export class AccountDialogController {
982
982
  * consumer's commit funnel (durable persist + hydration); falls back to
983
983
  * `SessionClient.registerAndActivate` (registration + activation only).
984
984
  *
985
- * A SWITCH (`opts.fromSwitch`) uses the IN-PLACE `commitSwitchedSession` funnel
986
- * so it never runs the cross-origin hub-sync redirect; a SIGN-IN uses
987
- * `commitSession` (which may hub-sync on an official web origin). When the
988
- * switch funnel is not wired it falls back to the sign-in funnel, then to
989
- * `registerAndActivate`.
985
+ * A SWITCH (`opts.fromSwitch`) uses the IN-PLACE `commitSwitchedSession` funnel;
986
+ * a SIGN-IN uses `commitSession`. When the switch funnel is not wired it falls
987
+ * back to the sign-in funnel, then to `registerAndActivate`.
990
988
  */
991
989
  async commitAuthorizedSession(session, user, opts) {
992
990
  const commit = opts?.fromSwitch
@@ -145,10 +145,6 @@ export const OXY_OAUTH_STATE_STORAGE_KEY = 'oxy_oauth_state';
145
145
  export const OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = 'oxy_oauth_code_verifier';
146
146
  /** `sessionStorage` key — the exact `redirect_uri` sent on the authorize request. */
147
147
  export const OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = 'oxy.oauth_redirect_uri';
148
- /** `sessionStorage` key — at most one silent OAuth attempt per navigation. */
149
- export const OXY_SILENT_OAUTH_ATTEMPTED_KEY = 'oxy.silent_oauth_attempted';
150
- /** `sessionStorage` key — blocks further cross-origin auto-restore in this tab. */
151
- export const OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = 'oxy.cross_origin_restore_attempted';
152
148
  /**
153
149
  * `sessionStorage` key for the in-app path to return to after an authorize
154
150
  * round trip. See {@link persistOAuthReturnPath}.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Official first-party web origin allowlist — shared by hub-ticket issuance,
3
- * OAuth redirect validation, and cross-origin session restore.
2
+ * Official first-party web origin allowlist — shared by OAuth redirect
3
+ * validation and the server-side trusted-origin checks.
4
4
  */
5
5
  import { CENTRAL_IDP_APEX } from './authWebUrl.js';
6
6
  import { registrableApex } from './registrableApex.js';
@@ -17,30 +17,9 @@ const OFFICIAL_APEXES = new Set([
17
17
  'moovo.now',
18
18
  'mercaria.co',
19
19
  ]);
20
- export function buildIdpHubOrigin() {
21
- return `https://auth.${CENTRAL_IDP_APEX}`;
22
- }
23
- /** Whether the current web origin is the central IdP hub (`auth.oxy.so`). */
24
- export function isIdpHubOrigin() {
25
- if (typeof globalThis === 'undefined') {
26
- return false;
27
- }
28
- const location = globalThis.location;
29
- if (!location) {
30
- return false;
31
- }
32
- try {
33
- const { hostname } = new URL(location.href);
34
- return hostname === `auth.${CENTRAL_IDP_APEX}`;
35
- }
36
- catch {
37
- return false;
38
- }
39
- }
40
20
  /**
41
21
  * Whether an origin is a loopback / local-dev origin (`localhost`, `127.0.0.1`,
42
- * or `[::1]` on any port, http or https). Local dev must never be bounced to a
43
- * hosted IdP for cross-origin session restore.
22
+ * or `[::1]` on any port, http or https).
44
23
  */
45
24
  export function isLoopbackOrigin(origin) {
46
25
  try {
@@ -76,49 +55,5 @@ export function isOfficialWebOrigin(origin) {
76
55
  return false;
77
56
  }
78
57
  }
79
- /** Normalize and validate a return URL against official origins. Returns origin only. */
80
- export function normalizeOfficialReturnOrigin(raw) {
81
- try {
82
- const parsed = new URL(raw);
83
- if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
84
- return null;
85
- }
86
- if (!isOfficialWebOrigin(parsed.origin)) {
87
- return null;
88
- }
89
- return parsed.origin;
90
- }
91
- catch {
92
- return null;
93
- }
94
- }
95
- /** Validate a hub-sync return URL; returns the full normalized URL string. */
96
- export function parseHubSyncReturnUrl(raw) {
97
- if (!raw) {
98
- return null;
99
- }
100
- try {
101
- const parsed = new URL(raw);
102
- if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
103
- return null;
104
- }
105
- if (!isOfficialWebOrigin(parsed.origin)) {
106
- return null;
107
- }
108
- return parsed.toString();
109
- }
110
- catch {
111
- return null;
112
- }
113
- }
114
- /** Build auth.oxy.so/sync redirect URL with a one-time hub ticket. */
115
- export function buildHubSyncUrl(ticket, returnUrl) {
116
- const url = new URL('/sync', buildIdpHubOrigin());
117
- url.searchParams.set('ticket', ticket);
118
- if (returnUrl) {
119
- url.searchParams.set('return', returnUrl);
120
- }
121
- return url.toString();
122
- }
123
58
  /** @deprecated Use {@link isOfficialWebOrigin}. */
124
59
  export const isAllowedDeviceJoinOrigin = isOfficialWebOrigin;