@oxyhq/core 3.4.1 → 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 +24 -2
  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
@@ -1,27 +1,12 @@
1
- import { OxyAuthenticationError } from '../OxyServices.errors.js';
2
- import { createDebugLogger } from '../shared/utils/debugUtils.js';
3
- const debug = createDebugLogger('PopupAuth');
1
+ import { OxyAuthenticationError } from "../OxyServices.errors.js";
2
+ import { createDebugLogger } from "../shared/utils/debugUtils.js";
3
+ const debug = createDebugLogger("PopupAuth");
4
4
  /**
5
- * Popup-based Cross-Domain Authentication Mixin
5
+ * Cross-domain browser auth helpers.
6
6
  *
7
- * Implements OAuth2-style authentication using popup windows and postMessage.
8
- * This is the primary authentication method for modern browsers, providing a
9
- * Google-like experience without full page redirects.
10
- *
11
- * Flow:
12
- * 1. Opens small popup window to auth.oxy.so
13
- * 2. User signs in (auth.oxy.so sets its own first-party cookie)
14
- * 3. auth.oxy.so sends token back via postMessage
15
- * 4. Popup closes, parent app has the session
16
- *
17
- * Features:
18
- * - No full page redirect (preserves app state)
19
- * - Works across different domains (homiio.com, mention.earth, etc.)
20
- * - Silent refresh using hidden iframe for seamless SSO
21
- * - CSRF protection via state parameter
22
- * - XSS protection via origin validation
23
- *
24
- * Browser Support: All modern browsers (IE11+)
7
+ * Popup sign-in is intentionally fail-closed in the clean session model because
8
+ * the historical implementation required bearer-token callback URLs. FedCM,
9
+ * redirect SSO, and silent iframe SSO are the supported browser paths.
25
10
  */
26
11
  export function OxyServicesPopupAuthMixin(Base) {
27
12
  var _a;
@@ -31,125 +16,25 @@ export function OxyServicesPopupAuthMixin(Base) {
31
16
  }
32
17
  /** Resolve auth URL from config or static default (method, not getter — getters break in TS mixins) */
33
18
  resolveAuthUrl() {
34
- return this.config.authWebUrl || this.constructor.DEFAULT_AUTH_URL;
19
+ return (this.config.authWebUrl || this.constructor.DEFAULT_AUTH_URL);
35
20
  }
36
21
  /**
37
- * Sign in using popup window
38
- *
39
- * Opens a centered popup window to auth.oxy.so where the user can sign in.
40
- * The popup automatically closes after successful authentication and the
41
- * session is returned to the parent window.
42
- *
43
- * @param options - Popup configuration options
44
- * @returns Session with access token and user data
45
- * @throws {OxyAuthenticationError} If popup is blocked or auth fails
46
- *
47
- * @example
48
- * ```typescript
49
- * const handleSignIn = async () => {
50
- * try {
51
- * const session = await oxyServices.signInWithPopup();
52
- * console.log('Signed in:', session.user);
53
- * } catch (error) {
54
- * if (error.message.includes('blocked')) {
55
- * alert('Please allow popups for this site');
56
- * }
57
- * }
58
- * };
59
- * ```
22
+ * Removed popup sign-in. Closes a caller-supplied popup handle and throws.
60
23
  */
61
24
  async signInWithPopup(options = {}) {
62
- if (typeof window === 'undefined') {
63
- throw new OxyAuthenticationError('Popup authentication requires browser environment');
64
- }
65
- const state = this.generateState();
66
- const nonce = this.generateNonce();
67
- // Store state for CSRF protection
68
- this.storeAuthState(state, nonce);
69
- const width = options.width || this.constructor.POPUP_WIDTH;
70
- const height = options.height || this.constructor.POPUP_HEIGHT;
71
- const timeout = options.timeout || this.constructor.POPUP_TIMEOUT;
72
- const mode = options.mode || 'login';
73
- const authUrl = this.buildAuthUrl({
74
- mode,
75
- state,
76
- nonce,
77
- clientId: window.location.origin,
78
- redirectUri: `${this.resolveAuthUrl()}/auth/callback`,
79
- });
80
- // If the caller pre-opened a popup on the raw user gesture (recommended
81
- // path — see `openBlankPopup` and `PopupAuthOptions.popup`), navigate it
82
- // to the auth URL instead of issuing a fresh `window.open` (which would
83
- // be blocked once any prior `await` has consumed the user activation).
84
- let popup;
85
- const preOpened = options.popup ?? null;
86
- if (preOpened) {
87
- if (preOpened.closed) {
88
- // The pre-opened popup is gone — distinguish a user cancel (they
89
- // closed the blank window before sign-in could navigate it) from a
90
- // blocker rejection. Lumping these together as "Popup blocked" is
91
- // misleading: the popup was NOT blocked, it was opened successfully
92
- // and then dismissed.
93
- throw new OxyAuthenticationError('Sign-in window was closed before authentication could start.');
94
- }
95
- try {
96
- preOpened.location.replace(authUrl);
97
- }
98
- catch (replaceError) {
99
- // `location.replace` can throw in sandboxed / cross-origin-locked
100
- // environments. Fall back to `href` assignment, which is more
101
- // permissive. Logged at debug-level so consumers can correlate
102
- // unusual sign-in behaviour without producing noise in normal flows.
103
- debug.warn('location.replace failed, falling back to location.href', replaceError);
104
- preOpened.location.href = authUrl;
105
- }
106
- popup = preOpened;
107
- }
108
- else {
109
- popup = this.openCenteredPopup(authUrl, 'Oxy Sign In', width, height);
25
+ if (typeof window === "undefined") {
26
+ throw new OxyAuthenticationError("Popup authentication requires browser environment");
110
27
  }
111
- if (!popup) {
112
- throw new OxyAuthenticationError('Popup blocked. Please allow popups for this site and try again.');
113
- }
114
- try {
115
- const session = await this.waitForPopupAuth(popup, state, timeout);
116
- // Store access token if present
117
- if (session && session.accessToken) {
118
- this.httpService.setTokens(session.accessToken);
119
- }
120
- // Fetch user data using the session ID
121
- // The callback page only sends sessionId/accessToken, not user data
122
- if (session && session.sessionId && !session.user) {
123
- try {
124
- const userData = await this.makeRequest('GET', `/session/user/${session.sessionId}`, undefined, { cache: false });
125
- if (userData) {
126
- session.user = userData;
127
- }
128
- }
129
- catch (userError) {
130
- debug.warn('Failed to fetch user data:', userError);
131
- // Continue without user data - caller can fetch separately
132
- }
133
- }
134
- return session;
135
- }
136
- catch (error) {
137
- throw error;
138
- }
139
- finally {
140
- this.clearAuthState(state);
28
+ if (options.popup && !options.popup.closed) {
29
+ options.popup.close();
141
30
  }
31
+ throw new OxyAuthenticationError("Popup authentication has been removed because it required access-token callback URLs. Use FedCM or redirect authentication.");
142
32
  }
143
33
  /**
144
- * Sign up using popup window
145
- *
146
- * Same as signInWithPopup but opens the signup page by default.
147
- *
148
- * @param options - Popup configuration options
149
- * @returns Session with access token and user data
34
+ * Removed popup signup. Closes a caller-supplied popup handle and throws.
150
35
  */
151
36
  async signUpWithPopup(options = {}) {
152
- return this.signInWithPopup({ ...options, mode: 'signup' });
37
+ return this.signInWithPopup({ ...options, mode: "signup" });
153
38
  }
154
39
  /**
155
40
  * Silent sign-in using hidden iframe
@@ -159,7 +44,7 @@ export function OxyServicesPopupAuthMixin(Base) {
159
44
  *
160
45
  * How it works:
161
46
  * 1. Creates hidden iframe pointing to auth.oxy.so/silent-auth
162
- * 2. If user has valid session at auth.oxy.so, it sends token via postMessage
47
+ * 2. If user has valid session at auth.oxy.so, it exchanges an opaque SSO code
163
48
  * 3. If not, iframe responds with null (no error thrown)
164
49
  *
165
50
  * This should be called on app startup to check for existing sessions.
@@ -181,7 +66,7 @@ export function OxyServicesPopupAuthMixin(Base) {
181
66
  * ```
182
67
  */
183
68
  async silentSignIn(options = {}) {
184
- if (typeof window === 'undefined') {
69
+ if (typeof window === "undefined") {
185
70
  return null;
186
71
  }
187
72
  const timeout = options.timeout || this.constructor.SILENT_TIMEOUT;
@@ -195,13 +80,15 @@ export function OxyServicesPopupAuthMixin(Base) {
195
80
  const authOrigin = options.authWebUrlOverride && options.authWebUrlOverride.length > 0
196
81
  ? options.authWebUrlOverride
197
82
  : this.resolveAuthUrl();
198
- const iframe = document.createElement('iframe');
199
- iframe.style.display = 'none';
200
- iframe.style.position = 'absolute';
201
- iframe.style.width = '0';
202
- iframe.style.height = '0';
203
- iframe.style.border = 'none';
204
- const silentUrl = `${authOrigin}/auth/silent?` + `client_id=${encodeURIComponent(clientId)}&` + `nonce=${nonce}`;
83
+ const iframe = document.createElement("iframe");
84
+ iframe.style.display = "none";
85
+ iframe.style.position = "absolute";
86
+ iframe.style.width = "0";
87
+ iframe.style.height = "0";
88
+ iframe.style.border = "none";
89
+ const silentUrl = `${authOrigin}/auth/silent?` +
90
+ `client_id=${encodeURIComponent(clientId)}&` +
91
+ `nonce=${nonce}`;
205
92
  iframe.src = silentUrl;
206
93
  document.body.appendChild(iframe);
207
94
  try {
@@ -214,7 +101,9 @@ export function OxyServicesPopupAuthMixin(Base) {
214
101
  // -> 401 -> token-clear loop in consumer apps because callers gated
215
102
  // on `session?.user` and never installed the user via
216
103
  // `handleAuthSuccess`, while HttpService quietly held the token.
217
- const accessToken = session ? session.accessToken : undefined;
104
+ const accessToken = session
105
+ ? session.accessToken
106
+ : undefined;
218
107
  if (!session || !accessToken || !session.sessionId) {
219
108
  return null;
220
109
  }
@@ -232,14 +121,14 @@ export function OxyServicesPopupAuthMixin(Base) {
232
121
  // missing-session response.
233
122
  if (!session.user) {
234
123
  try {
235
- const userData = await this.makeRequest('GET', `/session/user/${session.sessionId}`, undefined, { cache: false, retry: false });
124
+ const userData = await this.makeRequest("GET", `/session/user/${session.sessionId}`, undefined, { cache: false, retry: false });
236
125
  if (!userData) {
237
- throw new Error('Empty user response');
126
+ throw new Error("Empty user response");
238
127
  }
239
128
  session.user = userData;
240
129
  }
241
130
  catch (userError) {
242
- debug.warn('silentSignIn: failed to fetch user data, rolling back token', userError);
131
+ debug.warn("silentSignIn: failed to fetch user data, rolling back token", userError);
243
132
  if (previousAccessToken) {
244
133
  this.httpService.setTokens(previousAccessToken);
245
134
  }
@@ -261,32 +150,17 @@ export function OxyServicesPopupAuthMixin(Base) {
261
150
  /**
262
151
  * Open a blank, centered popup window SYNCHRONOUSLY.
263
152
  *
264
- * Use this in a click (or other user-gesture) handler BEFORE any `await`
265
- * to capture the transient user-activation. Pass the returned handle into
266
- * `signInWithPopup({ popup })` once the async portion of the flow runs.
267
- *
268
- * Returns `null` if the browser's popup blocker rejected the open.
269
- *
270
- * @example
271
- * ```typescript
272
- * const onSignInClick = () => {
273
- * const popup = oxyServices.openBlankPopup();
274
- * (async () => {
275
- * const silent = await oxyServices.silentSignInWithFedCM();
276
- * if (silent) { popup?.close(); return; }
277
- * await oxyServices.signInWithPopup({ popup });
278
- * })();
279
- * };
280
- * ```
153
+ * Kept only so legacy callers can pass a handle to the removed popup method,
154
+ * which closes it before throwing. New auth code should use FedCM or redirect.
281
155
  */
282
156
  openBlankPopup(width, height) {
283
- if (typeof window === 'undefined') {
157
+ if (typeof window === "undefined") {
284
158
  return null;
285
159
  }
286
160
  const ctor = this.constructor;
287
161
  const w = width ?? ctor.POPUP_WIDTH;
288
162
  const h = height ?? ctor.POPUP_HEIGHT;
289
- return this.openCenteredPopup('about:blank', 'Oxy Sign In', w, h);
163
+ return this.openCenteredPopup("about:blank", "Oxy Sign In", w, h);
290
164
  }
291
165
  /**
292
166
  * Open a centered popup window
@@ -301,86 +175,15 @@ export function OxyServicesPopupAuthMixin(Base) {
301
175
  `height=${height}`,
302
176
  `left=${left}`,
303
177
  `top=${top}`,
304
- 'toolbar=no',
305
- 'menubar=no',
306
- 'scrollbars=yes',
307
- 'resizable=yes',
308
- 'status=no',
309
- 'location=no',
310
- ].join(',');
178
+ "toolbar=no",
179
+ "menubar=no",
180
+ "scrollbars=yes",
181
+ "resizable=yes",
182
+ "status=no",
183
+ "location=no",
184
+ ].join(",");
311
185
  return window.open(url, title, features);
312
186
  }
313
- /**
314
- * Wait for authentication response from popup
315
- *
316
- * @private
317
- */
318
- async waitForPopupAuth(popup, expectedState, timeout) {
319
- return new Promise((resolve, reject) => {
320
- const timeoutId = setTimeout(() => {
321
- cleanup();
322
- reject(new OxyAuthenticationError('Authentication timeout'));
323
- }, timeout);
324
- const messageHandler = (event) => {
325
- const authUrl = this.resolveAuthUrl();
326
- // Log all messages for debugging
327
- if (event.data && typeof event.data === 'object' && event.data.type) {
328
- debug.log('Message received:', {
329
- origin: event.origin,
330
- expectedOrigin: authUrl,
331
- type: event.data.type,
332
- hasSession: !!event.data.session,
333
- hasError: !!event.data.error,
334
- });
335
- }
336
- // CRITICAL: Verify origin to prevent XSS attacks
337
- if (event.origin !== authUrl) {
338
- return;
339
- }
340
- const { type, state, session, error } = event.data;
341
- if (type !== 'oxy_auth_response') {
342
- return;
343
- }
344
- debug.log('Valid auth response:', { state, expectedState, hasSession: !!session, error });
345
- // Verify state parameter to prevent CSRF attacks
346
- if (state !== expectedState) {
347
- cleanup();
348
- debug.error('State mismatch');
349
- reject(new OxyAuthenticationError('Invalid state parameter. Possible CSRF attack.'));
350
- return;
351
- }
352
- cleanup();
353
- if (error) {
354
- debug.error('Auth error:', error);
355
- reject(new OxyAuthenticationError(error));
356
- }
357
- else if (session) {
358
- debug.log('Session received successfully');
359
- resolve(session);
360
- }
361
- else {
362
- debug.error('No session in response');
363
- reject(new OxyAuthenticationError('No session received from authentication server'));
364
- }
365
- };
366
- // Poll to detect if user closed the popup
367
- const pollInterval = setInterval(() => {
368
- if (popup.closed) {
369
- cleanup();
370
- reject(new OxyAuthenticationError('Authentication cancelled by user'));
371
- }
372
- }, 500);
373
- const cleanup = () => {
374
- clearTimeout(timeoutId);
375
- clearInterval(pollInterval);
376
- window.removeEventListener('message', messageHandler);
377
- if (!popup.closed) {
378
- popup.close();
379
- }
380
- };
381
- window.addEventListener('message', messageHandler);
382
- });
383
- }
384
187
  /**
385
188
  * Wait for authentication response from iframe
386
189
  *
@@ -401,7 +204,7 @@ export function OxyServicesPopupAuthMixin(Base) {
401
204
  return;
402
205
  }
403
206
  const { type, session } = event.data;
404
- if (type !== 'oxy_silent_auth') {
207
+ if (type !== "oxy_silent_auth") {
405
208
  return;
406
209
  }
407
210
  cleanup();
@@ -425,83 +228,31 @@ export function OxyServicesPopupAuthMixin(Base) {
425
228
  clearTimeout(timeoutId);
426
229
  iframe.onerror = null;
427
230
  iframe.onabort = null;
428
- window.removeEventListener('message', messageHandler);
231
+ window.removeEventListener("message", messageHandler);
429
232
  };
430
- window.addEventListener('message', messageHandler);
233
+ window.addEventListener("message", messageHandler);
431
234
  });
432
235
  }
433
- /**
434
- * Build authentication URL with query parameters
435
- *
436
- * @private
437
- */
438
- buildAuthUrl(params) {
439
- const url = new URL(`${this.resolveAuthUrl()}/${params.mode}`);
440
- url.searchParams.set('response_type', 'token');
441
- url.searchParams.set('client_id', params.clientId);
442
- url.searchParams.set('redirect_uri', params.redirectUri);
443
- url.searchParams.set('state', params.state);
444
- url.searchParams.set('nonce', params.nonce);
445
- return url.toString();
446
- }
447
- /**
448
- * Generate cryptographically secure state for CSRF protection
449
- *
450
- * @private
451
- */
452
- generateState() {
453
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
454
- return crypto.randomUUID();
455
- }
456
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
457
- const bytes = new Uint8Array(16);
458
- crypto.getRandomValues(bytes);
459
- return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
460
- }
461
- throw new Error('No secure random source available for state generation');
462
- }
463
236
  /**
464
237
  * Generate nonce for replay attack prevention
465
238
  *
466
239
  * @private
467
240
  */
468
241
  generateNonce() {
469
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
242
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
470
243
  return crypto.randomUUID();
471
244
  }
472
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
245
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
473
246
  const bytes = new Uint8Array(16);
474
247
  crypto.getRandomValues(bytes);
475
- return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
476
- }
477
- throw new Error('No secure random source available for nonce generation');
478
- }
479
- /**
480
- * Store auth state in session storage
481
- *
482
- * @private
483
- */
484
- storeAuthState(state, nonce) {
485
- if (typeof window !== 'undefined' && window.sessionStorage) {
486
- sessionStorage.setItem(`oxy_auth_state_${state}`, JSON.stringify({ nonce, timestamp: Date.now() }));
487
- }
488
- }
489
- /**
490
- * Clear auth state from session storage
491
- *
492
- * @private
493
- */
494
- clearAuthState(state) {
495
- if (typeof window !== 'undefined' && window.sessionStorage) {
496
- sessionStorage.removeItem(`oxy_auth_state_${state}`);
248
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
497
249
  }
250
+ throw new Error("No secure random source available for nonce generation");
498
251
  }
499
252
  },
500
- _a.DEFAULT_AUTH_URL = 'https://auth.oxy.so',
253
+ _a.DEFAULT_AUTH_URL = "https://auth.oxy.so",
501
254
  _a.POPUP_WIDTH = 500,
502
255
  _a.POPUP_HEIGHT = 700,
503
- _a.POPUP_TIMEOUT = 60000 // 1 minute
504
- ,
505
256
  _a.SILENT_TIMEOUT = 5000 // 5 seconds
506
257
  ,
507
258
  _a;