@oxyhq/core 3.4.1 → 3.4.3

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,6 +1,13 @@
1
1
  import type { OxyServicesBase } from '../OxyServices.base';
2
2
  import { OxyAuthenticationError } from '../OxyServices.errors';
3
3
  import type { SessionLoginResponse } from '../models/session';
4
+ import {
5
+ buildSsoBounceUrl,
6
+ ssoAttemptedKey,
7
+ ssoDestKey,
8
+ ssoGuardKey,
9
+ ssoStateKey,
10
+ } from '../utils/ssoBounce';
4
11
 
5
12
  export interface RedirectAuthOptions {
6
13
  redirectUri?: string;
@@ -9,403 +16,107 @@ export interface RedirectAuthOptions {
9
16
  }
10
17
 
11
18
  /**
12
- * Redirect-based Cross-Domain Authentication Mixin
19
+ * Redirect-based authentication without bearer tokens in URLs.
13
20
  *
14
- * Implements traditional OAuth2 redirect flow as a fallback when popup or
15
- * FedCM are not available or fail (e.g., mobile browsers, popup blockers).
16
- *
17
- * Flow:
18
- * 1. Save current URL
19
- * 2. Redirect to auth.oxy.so/login
20
- * 3. User signs in
21
- * 4. Redirect back with token in URL
22
- * 5. Extract token, restore session, clean URL
23
- *
24
- * Features:
25
- * - Works on all browsers (including old mobile browsers)
26
- * - Automatic URL cleanup after auth
27
- * - State preservation option
28
- * - CSRF protection via state parameter
29
- *
30
- * Trade-offs:
31
- * - Loses JavaScript app state (full page navigation)
32
- * - Visible redirect (user sees navigation)
33
- * - Slower perceived performance
21
+ * The redirect fallback now uses the same central SSO code-return contract as
22
+ * cold boot: the RP stores CSRF/destination state in sessionStorage, navigates
23
+ * to the central IdP, receives only an opaque one-time code in the URL fragment,
24
+ * and the provider's `sso-return` step exchanges that code for the real session.
34
25
  */
35
26
  export function OxyServicesRedirectAuthMixin<T extends typeof OxyServicesBase>(Base: T) {
36
27
  return class extends Base {
37
28
  constructor(...args: any[]) {
38
29
  super(...(args as [any]));
39
30
  }
40
- public static readonly DEFAULT_AUTH_URL = 'https://auth.oxy.so';
41
- public static readonly TOKEN_STORAGE_KEY = 'oxy_access_token';
42
- public static readonly SESSION_STORAGE_KEY = 'oxy_session_id';
43
- public static readonly STATE_STORAGE_KEY = 'oxy_auth_state';
44
- public static readonly PRE_AUTH_URL_KEY = 'oxy_pre_auth_url';
45
- public static readonly NONCE_STORAGE_KEY = 'oxy_auth_nonce';
46
-
47
- /**
48
- * Sign in using full page redirect
49
- *
50
- * Redirects the user to auth.oxy.so for authentication. After successful
51
- * sign-in, the user will be redirected back to the current page (or custom
52
- * redirect URI) with authentication tokens in the URL.
53
- *
54
- * Call handleAuthCallback() on app startup to complete the flow.
55
- *
56
- * @param options - Redirect configuration options
57
- *
58
- * @example
59
- * ```typescript
60
- * // Initiate sign-in
61
- * const handleSignIn = () => {
62
- * oxyServices.signInWithRedirect();
63
- * };
64
- *
65
- * // Handle callback on app startup
66
- * useEffect(() => {
67
- * const session = oxyServices.handleAuthCallback();
68
- * if (session) {
69
- * setUser(session.user);
70
- * }
71
- * }, []);
72
- * ```
73
- */
74
- signInWithRedirect(options: RedirectAuthOptions = {}): void {
75
- if (typeof window === 'undefined') {
76
- throw new OxyAuthenticationError('Redirect authentication requires browser environment');
77
- }
78
-
79
- const redirectUri = options.redirectUri || window.location.href;
80
- const mode = options.mode || 'login';
81
- const state = this.generateState();
82
- const nonce = this.generateNonce();
83
-
84
- // Store state for CSRF protection
85
- this.storeAuthState(state, nonce);
86
-
87
- // Save current URL to restore after auth (optional)
88
- if (options.preserveUrl !== false) {
89
- this.savePreAuthUrl(window.location.href);
90
- }
91
31
 
92
- const authUrl = this.buildAuthUrl({
93
- mode,
94
- redirectUri,
95
- state,
96
- nonce,
97
- clientId: window.location.origin,
98
- });
32
+ /**
33
+ * Start a full-page redirect through the central SSO flow.
34
+ *
35
+ * No access token, refresh token, or session id is ever put in the URL or
36
+ * localStorage. The caller's provider must run `consumeSsoReturn` on startup
37
+ * to complete the code exchange and commit the session.
38
+ */
39
+ signInWithRedirect(options: RedirectAuthOptions = {}): void {
40
+ if (typeof window === 'undefined' || typeof window.sessionStorage === 'undefined') {
41
+ throw new OxyAuthenticationError('Redirect authentication requires browser sessionStorage');
42
+ }
43
+
44
+ const origin = window.location.origin;
45
+ const state = this.generateState();
46
+ const destination = options.preserveUrl === false
47
+ ? (options.redirectUri || origin)
48
+ : (options.redirectUri || window.location.href);
49
+
50
+ window.sessionStorage.setItem(ssoStateKey(origin), state);
51
+ window.sessionStorage.setItem(ssoGuardKey(origin), String(Date.now()));
52
+ window.sessionStorage.setItem(ssoDestKey(origin), destination);
53
+ window.sessionStorage.setItem(ssoAttemptedKey(origin), '1');
54
+
55
+ window.location.assign(buildSsoBounceUrl(origin, state, this.config.authWebUrl));
56
+ }
57
+
58
+ signUpWithRedirect(options: RedirectAuthOptions = {}): void {
59
+ this.signInWithRedirect({ ...options, mode: 'signup' });
60
+ }
61
+
62
+ /**
63
+ * Legacy token-query callbacks are intentionally rejected. Modern providers
64
+ * complete redirect auth through `consumeSsoReturn`, which accepts only
65
+ * `#oxy_sso=ok&code=...`.
66
+ */
67
+ handleAuthCallback(): SessionLoginResponse | null {
68
+ if (typeof window === 'undefined') {
69
+ return null;
70
+ }
71
+
72
+ const url = new URL(window.location.href);
73
+ if (url.searchParams.has('access_token') || url.searchParams.has('session_id')) {
74
+ this.cleanAuthCallbackUrl(url);
75
+ throw new OxyAuthenticationError('Legacy access-token redirect callbacks are no longer accepted.');
76
+ }
99
77
 
100
- // Perform redirect
101
- window.location.href = authUrl;
102
- }
103
-
104
- /**
105
- * Sign up using full page redirect
106
- *
107
- * Same as signInWithRedirect but opens the signup page by default.
108
- */
109
- signUpWithRedirect(options: RedirectAuthOptions = {}): void {
110
- this.signInWithRedirect({ ...options, mode: 'signup' });
111
- }
112
-
113
- /**
114
- * Handle authentication callback
115
- *
116
- * Call this on app startup to check if the current page load is a
117
- * redirect back from the authentication server. If it is, this method
118
- * will extract the tokens, store them, and clean up the URL.
119
- *
120
- * @returns Session data if this is a callback, null otherwise
121
- * @throws {OxyAuthenticationError} If state validation fails (CSRF attack)
122
- *
123
- * @example
124
- * ```typescript
125
- * // In your app's root component or startup logic
126
- * useEffect(() => {
127
- * try {
128
- * const session = oxyServices.handleAuthCallback();
129
- * if (session) {
130
- * console.log('Logged in:', session.user);
131
- * setUser(session.user);
132
- * } else {
133
- * // Not a callback, check for existing session
134
- * const restored = oxyServices.restoreSession();
135
- * if (!restored) {
136
- * // No session, show login button
137
- * }
138
- * }
139
- * } catch (error) {
140
- * console.error('Auth callback failed:', error);
141
- * }
142
- * }, []);
143
- * ```
144
- */
145
- handleAuthCallback(): SessionLoginResponse | null {
146
- if (typeof window === 'undefined') {
147
78
  return null;
148
79
  }
149
80
 
150
- const url = new URL(window.location.href);
151
- const accessToken = url.searchParams.get('access_token');
152
- const sessionId = url.searchParams.get('session_id');
153
- const expiresAt = url.searchParams.get('expires_at');
154
- const state = url.searchParams.get('state');
155
- const error = url.searchParams.get('error');
156
- const errorDescription = url.searchParams.get('error_description');
157
-
158
- // Check if this is an error callback
159
- if (error) {
160
- this.clearAuthState();
161
- throw new OxyAuthenticationError(errorDescription || error);
162
- }
163
-
164
- // Check if this is an auth callback
165
- if (!accessToken || !sessionId) {
166
- return null; // Not a callback
167
- }
168
-
169
- // Verify state to prevent CSRF attacks
170
- const savedState = this.getStoredState();
171
- if (!savedState || state !== savedState) {
172
- this.clearAuthState();
173
- throw new OxyAuthenticationError('Invalid state parameter. Possible CSRF attack.');
174
- }
175
-
176
- // Store tokens
177
- this.storeTokens(accessToken, sessionId);
178
- this.httpService.setTokens(accessToken);
179
-
180
- // Build session response (minimal — full user data is fetched separately
181
- // by the caller via getCurrentUser() once tokens are stored).
182
- const session: SessionLoginResponse = {
183
- sessionId,
184
- deviceId: '', // Not available in redirect flow
185
- expiresAt: expiresAt || new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
186
- // Placeholder user — caller MUST fetch real user data via getCurrentUser()
187
- // before exposing this session to the application. The empty id signals
188
- // that the user payload has not yet been populated.
189
- user: { id: '', username: '' },
190
- };
191
-
192
- // Clean up URL (remove auth parameters)
193
- this.cleanAuthCallbackUrl(url);
194
-
195
- // Clean up storage
196
- this.clearAuthState();
197
-
198
- return session;
199
- }
200
-
201
- /**
202
- * Restore session from storage
203
- *
204
- * Attempts to restore a previously authenticated session from localStorage.
205
- * Call this on app startup if handleAuthCallback() returns null.
206
- *
207
- * @returns True if session was restored, false otherwise
208
- *
209
- * @example
210
- * ```typescript
211
- * useEffect(() => {
212
- * const session = oxyServices.handleAuthCallback();
213
- * if (!session) {
214
- * const restored = oxyServices.restoreSession();
215
- * if (!restored) {
216
- * // No session, user needs to sign in
217
- * setShowLogin(true);
218
- * }
219
- * }
220
- * }, []);
221
- * ```
222
- */
223
- restoreSession(): boolean {
224
- if (typeof window === 'undefined') {
81
+ restoreSession(): boolean {
225
82
  return false;
226
83
  }
227
84
 
228
- const token = localStorage.getItem((this.constructor as any).TOKEN_STORAGE_KEY);
229
- const sessionId = localStorage.getItem((this.constructor as any).SESSION_STORAGE_KEY);
230
-
231
- if (token && sessionId) {
232
- this.httpService.setTokens(token);
233
- return true;
234
- }
235
-
236
- return false;
237
- }
238
-
239
- /**
240
- * Clear stored session
241
- *
242
- * Removes all authentication data from storage. Call this on logout.
243
- */
244
- clearStoredSession(): void {
245
- if (typeof window === 'undefined') {
246
- return;
85
+ clearStoredSession(): void {
86
+ this.httpService.clearTokens();
247
87
  }
248
88
 
249
- localStorage.removeItem((this.constructor as any).TOKEN_STORAGE_KEY);
250
- localStorage.removeItem((this.constructor as any).SESSION_STORAGE_KEY);
251
- this.httpService.clearTokens();
252
- }
253
-
254
- /**
255
- * Get stored session ID
256
- */
257
- getStoredSessionId(): string | null {
258
- if (typeof window === 'undefined') {
89
+ getStoredSessionId(): string | null {
259
90
  return null;
260
91
  }
261
92
 
262
- return localStorage.getItem((this.constructor as any).SESSION_STORAGE_KEY);
263
- }
264
-
265
- /**
266
- * Build authentication URL with query parameters
267
- *
268
- * @private
269
- */
270
- public buildAuthUrl(params: {
271
- mode: string;
272
- redirectUri: string;
273
- state: string;
274
- nonce: string;
275
- clientId: string;
276
- }): string {
277
- const url = new URL(`${(this.config.authWebUrl || (this.constructor as any).DEFAULT_AUTH_URL)}/${params.mode}`);
278
- url.searchParams.set('redirect_uri', params.redirectUri);
279
- url.searchParams.set('state', params.state);
280
- url.searchParams.set('nonce', params.nonce);
281
- url.searchParams.set('client_id', params.clientId);
282
- url.searchParams.set('response_type', 'token');
283
- return url.toString();
284
- }
285
-
286
- /**
287
- * Store tokens in localStorage
288
- *
289
- * @private
290
- */
291
- public storeTokens(accessToken: string, sessionId: string): void {
292
- if (typeof window === 'undefined') {
293
- return;
294
- }
295
-
296
- localStorage.setItem((this.constructor as any).TOKEN_STORAGE_KEY, accessToken);
297
- localStorage.setItem((this.constructor as any).SESSION_STORAGE_KEY, sessionId);
298
- }
299
-
300
- /**
301
- * Generate cryptographically secure state for CSRF protection
302
- *
303
- * @private
304
- */
305
- public generateState(): string {
306
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
307
- return crypto.randomUUID();
308
- }
309
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
310
- const bytes = new Uint8Array(16);
311
- crypto.getRandomValues(bytes);
312
- return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
313
- }
314
- throw new Error('No secure random source available for state generation');
315
- }
316
-
317
- /**
318
- * Generate nonce for replay attack prevention
319
- *
320
- * @private
321
- */
322
- public generateNonce(): string {
323
- if (typeof crypto !== 'undefined' && crypto.randomUUID) {
324
- return crypto.randomUUID();
93
+ public generateState(): string {
94
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
95
+ return crypto.randomUUID();
96
+ }
97
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
98
+ const bytes = new Uint8Array(16);
99
+ crypto.getRandomValues(bytes);
100
+ return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
101
+ }
102
+ throw new Error('No secure random source available for state generation');
325
103
  }
326
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
327
- const bytes = new Uint8Array(16);
328
- crypto.getRandomValues(bytes);
329
- return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
330
- }
331
- throw new Error('No secure random source available for nonce generation');
332
- }
333
-
334
- /**
335
- * Store auth state in session storage
336
- *
337
- * @private
338
- */
339
- public storeAuthState(state: string, nonce: string): void {
340
- if (typeof window === 'undefined') {
341
- return;
342
- }
343
-
344
- sessionStorage.setItem((this.constructor as any).STATE_STORAGE_KEY, state);
345
- sessionStorage.setItem((this.constructor as any).NONCE_STORAGE_KEY, nonce);
346
- }
347
-
348
- /**
349
- * Get stored state
350
- *
351
- * @private
352
- */
353
- public getStoredState(): string | null {
354
- if (typeof window === 'undefined') {
355
- return null;
356
- }
357
-
358
- return sessionStorage.getItem((this.constructor as any).STATE_STORAGE_KEY);
359
- }
360
104
 
361
- /**
362
- * Clear auth state from storage
363
- *
364
- * @private
365
- */
366
- public clearAuthState(): void {
367
- if (typeof window === 'undefined') {
368
- return;
105
+ public generateNonce(): string {
106
+ return this.generateState();
369
107
  }
370
108
 
371
- sessionStorage.removeItem((this.constructor as any).STATE_STORAGE_KEY);
372
- sessionStorage.removeItem((this.constructor as any).NONCE_STORAGE_KEY);
373
- sessionStorage.removeItem((this.constructor as any).PRE_AUTH_URL_KEY);
374
- }
375
-
376
- /**
377
- * Save pre-authentication URL to restore later
378
- *
379
- * @private
380
- */
381
- public savePreAuthUrl(url: string): void {
382
- if (typeof window === 'undefined') {
383
- return;
109
+ public cleanAuthCallbackUrl(url: URL): void {
110
+ url.searchParams.delete('access_token');
111
+ url.searchParams.delete('session_id');
112
+ url.searchParams.delete('expires_at');
113
+ url.searchParams.delete('state');
114
+ url.searchParams.delete('nonce');
115
+ url.searchParams.delete('error');
116
+ url.searchParams.delete('error_description');
117
+ window.history.replaceState({}, '', url.toString());
384
118
  }
385
-
386
- sessionStorage.setItem((this.constructor as any).PRE_AUTH_URL_KEY, url);
387
- }
388
-
389
- /**
390
- * Clean authentication parameters from URL
391
- *
392
- * @private
393
- */
394
- public cleanAuthCallbackUrl(url: URL): void {
395
- // Remove auth parameters
396
- url.searchParams.delete('access_token');
397
- url.searchParams.delete('session_id');
398
- url.searchParams.delete('expires_at');
399
- url.searchParams.delete('state');
400
- url.searchParams.delete('nonce');
401
- url.searchParams.delete('error');
402
- url.searchParams.delete('error_description');
403
-
404
- // Update URL without reloading page
405
- window.history.replaceState({}, '', url.toString());
406
- }
407
119
  };
408
120
  }
409
121
 
410
- // Export the mixin function as both named and default
411
122
  export { OxyServicesRedirectAuthMixin as RedirectAuthMixin };