@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
@@ -43,7 +43,7 @@ export declare function OxyServicesLocationMixin<T extends typeof OxyServicesBas
43
43
  hitRate: number;
44
44
  };
45
45
  getCloudURL(): string;
46
- setTokens(accessToken: string, refreshToken?: string): void;
46
+ setTokens(accessToken: string): void;
47
47
  clearTokens(): void;
48
48
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
49
49
  _cachedUserId: string | null | undefined;
@@ -100,7 +100,7 @@ export declare function OxyServicesManagedAccountsMixin<T extends typeof OxyServ
100
100
  hitRate: number;
101
101
  };
102
102
  getCloudURL(): string;
103
- setTokens(accessToken: string, refreshToken?: string): void;
103
+ setTokens(accessToken: string): void;
104
104
  clearTokens(): void;
105
105
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
106
106
  _cachedUserId: string | null | undefined;
@@ -90,7 +90,7 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
90
90
  hitRate: number;
91
91
  };
92
92
  getCloudURL(): string;
93
- setTokens(accessToken: string, refreshToken?: string): void;
93
+ setTokens(accessToken: string): void;
94
94
  clearTokens(): void;
95
95
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
96
96
  _cachedUserId: string | null | undefined;
@@ -1,25 +1,17 @@
1
- import type { OxyServicesBase } from '../OxyServices.base';
2
- import type { SessionLoginResponse } from '../models/session';
1
+ import type { OxyServicesBase } from "../OxyServices.base";
2
+ import type { SessionLoginResponse } from "../models/session";
3
3
  export interface PopupAuthOptions {
4
+ /** Legacy option. Popup auth is removed; dimensions are ignored. */
4
5
  width?: number;
6
+ /** Legacy option. Popup auth is removed; dimensions are ignored. */
5
7
  height?: number;
8
+ /** Legacy option. Popup auth is removed; timeout is ignored. */
6
9
  timeout?: number;
7
- mode?: 'login' | 'signup';
10
+ /** Legacy option. Signup also fails closed. */
11
+ mode?: "login" | "signup";
8
12
  /**
9
- * A popup window handle the caller already opened SYNCHRONOUSLY inside the
10
- * user-gesture event handler (e.g. an `onClick`). The mixin will navigate
11
- * this window to the auth URL instead of calling `window.open` itself.
12
- *
13
- * Why this exists: Chrome (and other modern browsers) only honor
14
- * `window.open` while a transient user-activation is in scope. The
15
- * activation is consumed by the FIRST `await` in the click handler — so any
16
- * caller that awaits FedCM / silent SSO before reaching `signInWithPopup`
17
- * loses the activation and sees the popup blocked. The caller can dodge
18
- * this by opening a blank popup on the raw click via `openBlankPopup()`,
19
- * then passing the handle in here.
20
- *
21
- * `null` is accepted (and is the same as omitting the option) so consumers
22
- * can pass through the result of `openBlankPopup()` without an extra guard.
13
+ * A legacy popup window handle. `signInWithPopup` closes it and throws
14
+ * because popup auth has been removed.
23
15
  */
24
16
  popup?: Window | null;
25
17
  }
@@ -46,64 +38,22 @@ export interface SilentAuthOptions {
46
38
  authWebUrlOverride?: string;
47
39
  }
48
40
  /**
49
- * Popup-based Cross-Domain Authentication Mixin
50
- *
51
- * Implements OAuth2-style authentication using popup windows and postMessage.
52
- * This is the primary authentication method for modern browsers, providing a
53
- * Google-like experience without full page redirects.
54
- *
55
- * Flow:
56
- * 1. Opens small popup window to auth.oxy.so
57
- * 2. User signs in (auth.oxy.so sets its own first-party cookie)
58
- * 3. auth.oxy.so sends token back via postMessage
59
- * 4. Popup closes, parent app has the session
60
- *
61
- * Features:
62
- * - No full page redirect (preserves app state)
63
- * - Works across different domains (homiio.com, mention.earth, etc.)
64
- * - Silent refresh using hidden iframe for seamless SSO
65
- * - CSRF protection via state parameter
66
- * - XSS protection via origin validation
41
+ * Cross-domain browser auth helpers.
67
42
  *
68
- * Browser Support: All modern browsers (IE11+)
43
+ * Popup sign-in is intentionally fail-closed in the clean session model because
44
+ * the historical implementation required bearer-token callback URLs. FedCM,
45
+ * redirect SSO, and silent iframe SSO are the supported browser paths.
69
46
  */
70
47
  export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBase>(Base: T): {
71
48
  new (...args: any[]): {
72
49
  /** Resolve auth URL from config or static default (method, not getter — getters break in TS mixins) */
73
50
  resolveAuthUrl(): string;
74
51
  /**
75
- * Sign in using popup window
76
- *
77
- * Opens a centered popup window to auth.oxy.so where the user can sign in.
78
- * The popup automatically closes after successful authentication and the
79
- * session is returned to the parent window.
80
- *
81
- * @param options - Popup configuration options
82
- * @returns Session with access token and user data
83
- * @throws {OxyAuthenticationError} If popup is blocked or auth fails
84
- *
85
- * @example
86
- * ```typescript
87
- * const handleSignIn = async () => {
88
- * try {
89
- * const session = await oxyServices.signInWithPopup();
90
- * console.log('Signed in:', session.user);
91
- * } catch (error) {
92
- * if (error.message.includes('blocked')) {
93
- * alert('Please allow popups for this site');
94
- * }
95
- * }
96
- * };
97
- * ```
52
+ * Removed popup sign-in. Closes a caller-supplied popup handle and throws.
98
53
  */
99
54
  signInWithPopup(options?: PopupAuthOptions): Promise<SessionLoginResponse>;
100
55
  /**
101
- * Sign up using popup window
102
- *
103
- * Same as signInWithPopup but opens the signup page by default.
104
- *
105
- * @param options - Popup configuration options
106
- * @returns Session with access token and user data
56
+ * Removed popup signup. Closes a caller-supplied popup handle and throws.
107
57
  */
108
58
  signUpWithPopup(options?: PopupAuthOptions): Promise<SessionLoginResponse>;
109
59
  /**
@@ -114,7 +64,7 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
114
64
  *
115
65
  * How it works:
116
66
  * 1. Creates hidden iframe pointing to auth.oxy.so/silent-auth
117
- * 2. If user has valid session at auth.oxy.so, it sends token via postMessage
67
+ * 2. If user has valid session at auth.oxy.so, it exchanges an opaque SSO code
118
68
  * 3. If not, iframe responds with null (no error thrown)
119
69
  *
120
70
  * This should be called on app startup to check for existing sessions.
@@ -139,23 +89,8 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
139
89
  /**
140
90
  * Open a blank, centered popup window SYNCHRONOUSLY.
141
91
  *
142
- * Use this in a click (or other user-gesture) handler BEFORE any `await`
143
- * to capture the transient user-activation. Pass the returned handle into
144
- * `signInWithPopup({ popup })` once the async portion of the flow runs.
145
- *
146
- * Returns `null` if the browser's popup blocker rejected the open.
147
- *
148
- * @example
149
- * ```typescript
150
- * const onSignInClick = () => {
151
- * const popup = oxyServices.openBlankPopup();
152
- * (async () => {
153
- * const silent = await oxyServices.silentSignInWithFedCM();
154
- * if (silent) { popup?.close(); return; }
155
- * await oxyServices.signInWithPopup({ popup });
156
- * })();
157
- * };
158
- * ```
92
+ * Kept only so legacy callers can pass a handle to the removed popup method,
93
+ * which closes it before throwing. New auth code should use FedCM or redirect.
159
94
  */
160
95
  openBlankPopup(width?: number, height?: number): Window | null;
161
96
  /**
@@ -164,54 +99,18 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
164
99
  * @private
165
100
  */
166
101
  openCenteredPopup(url: string, title: string, width: number, height: number): Window | null;
167
- /**
168
- * Wait for authentication response from popup
169
- *
170
- * @private
171
- */
172
- waitForPopupAuth(popup: Window, expectedState: string, timeout: number): Promise<SessionLoginResponse>;
173
102
  /**
174
103
  * Wait for authentication response from iframe
175
104
  *
176
105
  * @private
177
106
  */
178
107
  waitForIframeAuth(iframe: HTMLIFrameElement, timeout: number, expectedOrigin: string): Promise<SessionLoginResponse | null>;
179
- /**
180
- * Build authentication URL with query parameters
181
- *
182
- * @private
183
- */
184
- buildAuthUrl(params: {
185
- mode: string;
186
- state: string;
187
- nonce: string;
188
- clientId: string;
189
- redirectUri: string;
190
- }): string;
191
- /**
192
- * Generate cryptographically secure state for CSRF protection
193
- *
194
- * @private
195
- */
196
- generateState(): string;
197
108
  /**
198
109
  * Generate nonce for replay attack prevention
199
110
  *
200
111
  * @private
201
112
  */
202
113
  generateNonce(): string;
203
- /**
204
- * Store auth state in session storage
205
- *
206
- * @private
207
- */
208
- storeAuthState(state: string, nonce: string): void;
209
- /**
210
- * Clear auth state from session storage
211
- *
212
- * @private
213
- */
214
- clearAuthState(state: string): void;
215
114
  httpService: import("../HttpService").HttpService;
216
115
  cloudURL: string;
217
116
  config: import("../OxyServices.base").OxyConfig;
@@ -266,7 +165,6 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
266
165
  readonly DEFAULT_AUTH_URL: "https://auth.oxy.so";
267
166
  readonly POPUP_WIDTH: 500;
268
167
  readonly POPUP_HEIGHT: 700;
269
- readonly POPUP_TIMEOUT: 60000;
270
168
  readonly SILENT_TIMEOUT: 5000;
271
169
  } & T;
272
170
  export { OxyServicesPopupAuthMixin as PopupAuthMixin };
@@ -101,7 +101,7 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
101
101
  hitRate: number;
102
102
  };
103
103
  getCloudURL(): string;
104
- setTokens(accessToken: string, refreshToken?: string): void;
104
+ setTokens(accessToken: string): void;
105
105
  clearTokens(): void;
106
106
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
107
107
  _cachedUserId: string | null | undefined;
@@ -6,190 +6,35 @@ export interface RedirectAuthOptions {
6
6
  preserveUrl?: boolean;
7
7
  }
8
8
  /**
9
- * Redirect-based Cross-Domain Authentication Mixin
9
+ * Redirect-based authentication without bearer tokens in URLs.
10
10
  *
11
- * Implements traditional OAuth2 redirect flow as a fallback when popup or
12
- * FedCM are not available or fail (e.g., mobile browsers, popup blockers).
13
- *
14
- * Flow:
15
- * 1. Save current URL
16
- * 2. Redirect to auth.oxy.so/login
17
- * 3. User signs in
18
- * 4. Redirect back with token in URL
19
- * 5. Extract token, restore session, clean URL
20
- *
21
- * Features:
22
- * - Works on all browsers (including old mobile browsers)
23
- * - Automatic URL cleanup after auth
24
- * - State preservation option
25
- * - CSRF protection via state parameter
26
- *
27
- * Trade-offs:
28
- * - Loses JavaScript app state (full page navigation)
29
- * - Visible redirect (user sees navigation)
30
- * - Slower perceived performance
11
+ * The redirect fallback now uses the same central SSO code-return contract as
12
+ * cold boot: the RP stores CSRF/destination state in sessionStorage, navigates
13
+ * to the central IdP, receives only an opaque one-time code in the URL fragment,
14
+ * and the provider's `sso-return` step exchanges that code for the real session.
31
15
  */
32
16
  export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyServicesBase>(Base: T): {
33
17
  new (...args: any[]): {
34
18
  /**
35
- * Sign in using full page redirect
36
- *
37
- * Redirects the user to auth.oxy.so for authentication. After successful
38
- * sign-in, the user will be redirected back to the current page (or custom
39
- * redirect URI) with authentication tokens in the URL.
40
- *
41
- * Call handleAuthCallback() on app startup to complete the flow.
42
- *
43
- * @param options - Redirect configuration options
44
- *
45
- * @example
46
- * ```typescript
47
- * // Initiate sign-in
48
- * const handleSignIn = () => {
49
- * oxyServices.signInWithRedirect();
50
- * };
19
+ * Start a full-page redirect through the central SSO flow.
51
20
  *
52
- * // Handle callback on app startup
53
- * useEffect(() => {
54
- * const session = oxyServices.handleAuthCallback();
55
- * if (session) {
56
- * setUser(session.user);
57
- * }
58
- * }, []);
59
- * ```
21
+ * No access token, refresh token, or session id is ever put in the URL or
22
+ * localStorage. The caller's provider must run `consumeSsoReturn` on startup
23
+ * to complete the code exchange and commit the session.
60
24
  */
61
25
  signInWithRedirect(options?: RedirectAuthOptions): void;
62
- /**
63
- * Sign up using full page redirect
64
- *
65
- * Same as signInWithRedirect but opens the signup page by default.
66
- */
67
26
  signUpWithRedirect(options?: RedirectAuthOptions): void;
68
27
  /**
69
- * Handle authentication callback
70
- *
71
- * Call this on app startup to check if the current page load is a
72
- * redirect back from the authentication server. If it is, this method
73
- * will extract the tokens, store them, and clean up the URL.
74
- *
75
- * @returns Session data if this is a callback, null otherwise
76
- * @throws {OxyAuthenticationError} If state validation fails (CSRF attack)
77
- *
78
- * @example
79
- * ```typescript
80
- * // In your app's root component or startup logic
81
- * useEffect(() => {
82
- * try {
83
- * const session = oxyServices.handleAuthCallback();
84
- * if (session) {
85
- * console.log('Logged in:', session.user);
86
- * setUser(session.user);
87
- * } else {
88
- * // Not a callback, check for existing session
89
- * const restored = oxyServices.restoreSession();
90
- * if (!restored) {
91
- * // No session, show login button
92
- * }
93
- * }
94
- * } catch (error) {
95
- * console.error('Auth callback failed:', error);
96
- * }
97
- * }, []);
98
- * ```
28
+ * Legacy token-query callbacks are intentionally rejected. Modern providers
29
+ * complete redirect auth through `consumeSsoReturn`, which accepts only
30
+ * `#oxy_sso=ok&code=...`.
99
31
  */
100
32
  handleAuthCallback(): SessionLoginResponse | null;
101
- /**
102
- * Restore session from storage
103
- *
104
- * Attempts to restore a previously authenticated session from localStorage.
105
- * Call this on app startup if handleAuthCallback() returns null.
106
- *
107
- * @returns True if session was restored, false otherwise
108
- *
109
- * @example
110
- * ```typescript
111
- * useEffect(() => {
112
- * const session = oxyServices.handleAuthCallback();
113
- * if (!session) {
114
- * const restored = oxyServices.restoreSession();
115
- * if (!restored) {
116
- * // No session, user needs to sign in
117
- * setShowLogin(true);
118
- * }
119
- * }
120
- * }, []);
121
- * ```
122
- */
123
33
  restoreSession(): boolean;
124
- /**
125
- * Clear stored session
126
- *
127
- * Removes all authentication data from storage. Call this on logout.
128
- */
129
34
  clearStoredSession(): void;
130
- /**
131
- * Get stored session ID
132
- */
133
35
  getStoredSessionId(): string | null;
134
- /**
135
- * Build authentication URL with query parameters
136
- *
137
- * @private
138
- */
139
- buildAuthUrl(params: {
140
- mode: string;
141
- redirectUri: string;
142
- state: string;
143
- nonce: string;
144
- clientId: string;
145
- }): string;
146
- /**
147
- * Store tokens in localStorage
148
- *
149
- * @private
150
- */
151
- storeTokens(accessToken: string, sessionId: string): void;
152
- /**
153
- * Generate cryptographically secure state for CSRF protection
154
- *
155
- * @private
156
- */
157
36
  generateState(): string;
158
- /**
159
- * Generate nonce for replay attack prevention
160
- *
161
- * @private
162
- */
163
37
  generateNonce(): string;
164
- /**
165
- * Store auth state in session storage
166
- *
167
- * @private
168
- */
169
- storeAuthState(state: string, nonce: string): void;
170
- /**
171
- * Get stored state
172
- *
173
- * @private
174
- */
175
- getStoredState(): string | null;
176
- /**
177
- * Clear auth state from storage
178
- *
179
- * @private
180
- */
181
- clearAuthState(): void;
182
- /**
183
- * Save pre-authentication URL to restore later
184
- *
185
- * @private
186
- */
187
- savePreAuthUrl(url: string): void;
188
- /**
189
- * Clean authentication parameters from URL
190
- *
191
- * @private
192
- */
193
38
  cleanAuthCallbackUrl(url: URL): void;
194
39
  httpService: import("../HttpService").HttpService;
195
40
  cloudURL: string;
@@ -217,7 +62,7 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
217
62
  hitRate: number;
218
63
  };
219
64
  getCloudURL(): string;
220
- setTokens(accessToken: string, refreshToken?: string): void;
65
+ setTokens(accessToken: string): void;
221
66
  clearTokens(): void;
222
67
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
223
68
  _cachedUserId: string | null | undefined;
@@ -242,11 +87,5 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
242
87
  [key: string]: any;
243
88
  }>;
244
89
  };
245
- readonly DEFAULT_AUTH_URL: "https://auth.oxy.so";
246
- readonly TOKEN_STORAGE_KEY: "oxy_access_token";
247
- readonly SESSION_STORAGE_KEY: "oxy_session_id";
248
- readonly STATE_STORAGE_KEY: "oxy_auth_state";
249
- readonly PRE_AUTH_URL_KEY: "oxy_pre_auth_url";
250
- readonly NONCE_STORAGE_KEY: "oxy_auth_nonce";
251
90
  } & T;
252
91
  export { OxyServicesRedirectAuthMixin as RedirectAuthMixin };
@@ -408,7 +408,7 @@ export declare function OxyServicesReputationMixin<T extends typeof OxyServicesB
408
408
  hitRate: number;
409
409
  };
410
410
  getCloudURL(): string;
411
- setTokens(accessToken: string, refreshToken?: string): void;
411
+ setTokens(accessToken: string): void;
412
412
  clearTokens(): void;
413
413
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
414
414
  _cachedUserId: string | null | undefined;
@@ -57,7 +57,7 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
57
57
  hitRate: number;
58
58
  };
59
59
  getCloudURL(): string;
60
- setTokens(accessToken: string, refreshToken?: string): void;
60
+ setTokens(accessToken: string): void;
61
61
  clearTokens(): void;
62
62
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
63
63
  _cachedUserId: string | null | undefined;
@@ -0,0 +1,131 @@
1
+ import type { OxyServicesBase } from "../OxyServices.base";
2
+ import type { SessionLoginResponse } from "../models/session";
3
+ export interface SilentAuthOptions {
4
+ timeout?: number;
5
+ /**
6
+ * Override the auth-web (IdP) origin used for the silent iframe, instead of
7
+ * the instance's configured `resolveAuthUrl()`.
8
+ *
9
+ * Why this exists: an instance configured with the CENTRAL IdP
10
+ * (`authWebUrl=https://auth.oxy.so`, for the opaque-code `/sso` bounce and
11
+ * FedCM) cannot read the DURABLE per-apex `fedcm_session` cookie via the
12
+ * central host — that cookie is first-party only on `auth.<rp-apex>` (e.g.
13
+ * `auth.mention.earth`). The cross-domain reload-restore path must point the
14
+ * `/auth/silent` iframe at the PER-APEX host so the cookie is same-site to
15
+ * the RP page (first-party under Safari ITP / Firefox TCP) and the restore
16
+ * is NOT a top-level navigation (no flash, works in a backgrounded tab).
17
+ *
18
+ * When provided this value is used BOTH for the iframe `src` AND for the
19
+ * `postMessage` origin validation in {@link waitForIframeAuth}, so the
20
+ * security check still matches the exact origin the iframe was loaded from.
21
+ * Must be an absolute origin (`https://auth.<apex>`); ignored if empty.
22
+ */
23
+ authWebUrlOverride?: string;
24
+ }
25
+ /**
26
+ * Cross-domain silent browser auth helpers.
27
+ *
28
+ * The clean session model supports FedCM, tokenless redirect SSO, and silent
29
+ * iframe SSO. Bearer-token callback URLs are not part of this surface.
30
+ */
31
+ export declare function OxyServicesSilentAuthMixin<T extends typeof OxyServicesBase>(Base: T): {
32
+ new (...args: any[]): {
33
+ /** Resolve auth URL from config or static default (method, not getter — getters break in TS mixins) */
34
+ resolveAuthUrl(): string;
35
+ /**
36
+ * Silent sign-in using hidden iframe
37
+ *
38
+ * Attempts to automatically re-authenticate the user without any UI.
39
+ * This is what enables seamless SSO across all Oxy domains.
40
+ *
41
+ * How it works:
42
+ * 1. Creates hidden iframe pointing to auth.oxy.so/silent-auth
43
+ * 2. If user has valid session at auth.oxy.so, it exchanges an opaque SSO code
44
+ * 3. If not, iframe responds with null (no error thrown)
45
+ *
46
+ * This should be called on app startup to check for existing sessions.
47
+ *
48
+ * @param options - Silent auth options
49
+ * @returns Session if user is signed in, null otherwise
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * useEffect(() => {
54
+ * const checkAuth = async () => {
55
+ * const session = await oxyServices.silentSignIn();
56
+ * if (session) {
57
+ * setUser(session.user);
58
+ * }
59
+ * };
60
+ * checkAuth();
61
+ * }, []);
62
+ * ```
63
+ */
64
+ silentSignIn(options?: SilentAuthOptions): Promise<SessionLoginResponse | null>;
65
+ /**
66
+ * Wait for authentication response from iframe
67
+ *
68
+ * @private
69
+ */
70
+ waitForIframeAuth(iframe: HTMLIFrameElement, timeout: number, expectedOrigin: string): Promise<SessionLoginResponse | null>;
71
+ /**
72
+ * Generate nonce for replay attack prevention
73
+ *
74
+ * @private
75
+ */
76
+ generateNonce(): string;
77
+ httpService: import("../HttpService").HttpService;
78
+ cloudURL: string;
79
+ config: import("../OxyServices.base").OxyConfig;
80
+ __resetTokensForTests(): void;
81
+ makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
82
+ getBaseURL(): string;
83
+ getSessionBaseUrl(): string;
84
+ getClient(): import("../HttpService").HttpService;
85
+ getMetrics(): {
86
+ totalRequests: number;
87
+ successfulRequests: number;
88
+ failedRequests: number;
89
+ cacheHits: number;
90
+ cacheMisses: number;
91
+ averageResponseTime: number;
92
+ };
93
+ clearCache(): void;
94
+ clearCacheEntry(key: string): void;
95
+ clearCacheByPrefix(prefix: string): number;
96
+ getCacheStats(): {
97
+ size: number;
98
+ hits: number;
99
+ misses: number;
100
+ hitRate: number;
101
+ };
102
+ getCloudURL(): string;
103
+ setTokens(accessToken: string): void;
104
+ clearTokens(): void;
105
+ onTokensChanged(listener: (accessToken: string | null) => void): () => void;
106
+ _cachedUserId: string | null | undefined;
107
+ _cachedAccessToken: string | null;
108
+ getCurrentUserId(): string | null;
109
+ hasValidToken(): boolean;
110
+ getAccessToken(): string | null;
111
+ setActingAs(userId: string | null): void;
112
+ getActingAs(): string | null;
113
+ waitForAuth(timeoutMs?: number): Promise<boolean>;
114
+ withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
115
+ maxRetries?: number;
116
+ retryDelay?: number;
117
+ authTimeoutMs?: number;
118
+ }): Promise<T_1>;
119
+ validate(): Promise<boolean>;
120
+ handleError(error: unknown): Error;
121
+ healthCheck(): Promise<{
122
+ status: string;
123
+ users?: number;
124
+ timestamp?: string;
125
+ [key: string]: any;
126
+ }>;
127
+ };
128
+ readonly DEFAULT_AUTH_URL: "https://auth.oxy.so";
129
+ readonly SILENT_TIMEOUT: 5000;
130
+ } & T;
131
+ export { OxyServicesSilentAuthMixin as SilentAuthMixin };
@@ -29,8 +29,7 @@ import type { SessionLoginResponse } from '../models/session';
29
29
  *
30
30
  * Exposed as a module-level helper (in addition to the instance method below)
31
31
  * so consumers that do not yet hold an `OxyServices` instance can still mint a
32
- * bounce state. Reuses the same `crypto.randomUUID`-based generator the popup
33
- * mixin uses for its CSRF state, with a `getRandomValues` fallback.
32
+ * bounce state. Uses `crypto.randomUUID` with a `getRandomValues` fallback.
34
33
  */
35
34
  export declare function generateSsoState(): string;
36
35
  export declare function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Base: T): {
@@ -38,8 +37,8 @@ export declare function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Ba
38
37
  /**
39
38
  * Generate cryptographically secure state for the SSO bounce (CSRF
40
39
  * protection). Delegates to the module-level {@link generateSsoState}
41
- * helper, which uses the same `crypto.randomUUID`-based generator the popup
42
- * mixin's `generateState()` uses — one shared secure-random implementation.
40
+ * helper, which uses `crypto.randomUUID` when available and falls back to
41
+ * `crypto.getRandomValues`.
43
42
  */
44
43
  generateSsoState(): string;
45
44
  /**
@@ -83,7 +82,7 @@ export declare function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Ba
83
82
  hitRate: number;
84
83
  };
85
84
  getCloudURL(): string;
86
- setTokens(accessToken: string, refreshToken?: string): void;
85
+ setTokens(accessToken: string): void;
87
86
  clearTokens(): void;
88
87
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
89
88
  _cachedUserId: string | null | undefined;
@@ -83,7 +83,7 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
83
83
  hitRate: number;
84
84
  };
85
85
  getCloudURL(): string;
86
- setTokens(accessToken: string, refreshToken?: string): void;
86
+ setTokens(accessToken: string): void;
87
87
  clearTokens(): void;
88
88
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
89
89
  _cachedUserId: string | null | undefined;
@@ -249,7 +249,7 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
249
249
  hitRate: number;
250
250
  };
251
251
  getCloudURL(): string;
252
- setTokens(accessToken: string, refreshToken?: string): void;
252
+ setTokens(accessToken: string): void;
253
253
  clearTokens(): void;
254
254
  onTokensChanged(listener: (accessToken: string | null) => void): () => void;
255
255
  _cachedUserId: string | null | undefined;