@oxyhq/core 3.4.0 → 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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/AuthManager.js +91 -319
- package/dist/cjs/CrossDomainAuth.js +19 -106
- package/dist/cjs/HttpService.js +49 -73
- package/dist/cjs/OxyServices.base.js +2 -2
- package/dist/cjs/i18n/index.js +7 -1
- package/dist/cjs/i18n/locales/ar-SA.json +18 -2
- package/dist/cjs/i18n/locales/ca-ES.json +18 -2
- package/dist/cjs/i18n/locales/de-DE.json +18 -2
- package/dist/cjs/i18n/locales/en-US.json +16 -2
- package/dist/cjs/i18n/locales/es-ES.json +16 -2
- package/dist/cjs/i18n/locales/fr-FR.json +18 -2
- package/dist/cjs/i18n/locales/it-IT.json +18 -2
- package/dist/cjs/i18n/locales/ja-JP.json +18 -2
- package/dist/cjs/i18n/locales/ko-KR.json +18 -2
- package/dist/cjs/i18n/locales/locales/ar-SA.json +18 -2
- package/dist/cjs/i18n/locales/locales/ca-ES.json +18 -2
- package/dist/cjs/i18n/locales/locales/de-DE.json +18 -2
- package/dist/cjs/i18n/locales/locales/en-US.json +17 -3
- package/dist/cjs/i18n/locales/locales/es-ES.json +16 -2
- package/dist/cjs/i18n/locales/locales/fr-FR.json +18 -2
- package/dist/cjs/i18n/locales/locales/it-IT.json +18 -2
- package/dist/cjs/i18n/locales/locales/ja-JP.json +18 -2
- package/dist/cjs/i18n/locales/locales/ko-KR.json +18 -2
- package/dist/cjs/i18n/locales/locales/pt-PT.json +18 -2
- package/dist/cjs/i18n/locales/locales/zh-CN.json +18 -2
- package/dist/cjs/i18n/locales/pt-PT.json +18 -2
- package/dist/cjs/i18n/locales/zh-CN.json +18 -2
- package/dist/cjs/mixins/OxyServices.auth.js +20 -63
- package/dist/cjs/mixins/OxyServices.fedcm.js +10 -12
- package/dist/cjs/mixins/OxyServices.popup.js +50 -299
- package/dist/cjs/mixins/OxyServices.redirect.js +84 -348
- package/dist/cjs/mixins/OxyServices.silent.js +204 -0
- package/dist/cjs/mixins/OxyServices.sso.js +4 -5
- package/dist/cjs/mixins/OxyServices.utility.js +6 -15
- package/dist/cjs/mixins/index.js +5 -6
- package/dist/cjs/server/index.js +21 -0
- package/dist/cjs/server/rateLimit.js +77 -0
- package/dist/cjs/shared/utils/debugUtils.js +1 -1
- package/dist/cjs/utils/accountUtils.js +4 -4
- package/dist/cjs/utils/authHelpers.js +21 -15
- package/dist/cjs/utils/coldBoot.js +3 -3
- package/dist/cjs/utils/fapiAutoDetect.js +1 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/AuthManager.js +91 -319
- package/dist/esm/CrossDomainAuth.js +19 -106
- package/dist/esm/HttpService.js +49 -73
- package/dist/esm/OxyServices.base.js +2 -2
- package/dist/esm/i18n/index.js +7 -1
- package/dist/esm/i18n/locales/ar-SA.json +18 -2
- package/dist/esm/i18n/locales/ca-ES.json +18 -2
- package/dist/esm/i18n/locales/de-DE.json +18 -2
- package/dist/esm/i18n/locales/en-US.json +16 -2
- package/dist/esm/i18n/locales/es-ES.json +16 -2
- package/dist/esm/i18n/locales/fr-FR.json +18 -2
- package/dist/esm/i18n/locales/it-IT.json +18 -2
- package/dist/esm/i18n/locales/ja-JP.json +18 -2
- package/dist/esm/i18n/locales/ko-KR.json +18 -2
- package/dist/esm/i18n/locales/locales/ar-SA.json +18 -2
- package/dist/esm/i18n/locales/locales/ca-ES.json +18 -2
- package/dist/esm/i18n/locales/locales/de-DE.json +18 -2
- package/dist/esm/i18n/locales/locales/en-US.json +17 -3
- package/dist/esm/i18n/locales/locales/es-ES.json +16 -2
- package/dist/esm/i18n/locales/locales/fr-FR.json +18 -2
- package/dist/esm/i18n/locales/locales/it-IT.json +18 -2
- package/dist/esm/i18n/locales/locales/ja-JP.json +18 -2
- package/dist/esm/i18n/locales/locales/ko-KR.json +18 -2
- package/dist/esm/i18n/locales/locales/pt-PT.json +18 -2
- package/dist/esm/i18n/locales/locales/zh-CN.json +18 -2
- package/dist/esm/i18n/locales/pt-PT.json +18 -2
- package/dist/esm/i18n/locales/zh-CN.json +18 -2
- package/dist/esm/mixins/OxyServices.auth.js +20 -63
- package/dist/esm/mixins/OxyServices.fedcm.js +10 -12
- package/dist/esm/mixins/OxyServices.popup.js +52 -301
- package/dist/esm/mixins/OxyServices.redirect.js +84 -349
- package/dist/esm/mixins/OxyServices.silent.js +202 -0
- package/dist/esm/mixins/OxyServices.sso.js +4 -5
- package/dist/esm/mixins/OxyServices.utility.js +6 -15
- package/dist/esm/mixins/index.js +5 -6
- package/dist/esm/server/index.js +17 -0
- package/dist/esm/server/rateLimit.js +71 -0
- package/dist/esm/shared/utils/debugUtils.js +1 -1
- package/dist/esm/utils/accountUtils.js +4 -4
- package/dist/esm/utils/authHelpers.js +21 -15
- package/dist/esm/utils/coldBoot.js +3 -3
- package/dist/esm/utils/fapiAutoDetect.js +1 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/AuthManager.d.ts +26 -53
- package/dist/types/AuthManagerTypes.d.ts +5 -9
- package/dist/types/CrossDomainAuth.d.ts +13 -52
- package/dist/types/HttpService.d.ts +9 -8
- package/dist/types/OxyServices.base.d.ts +1 -1
- package/dist/types/OxyServices.d.ts +4 -10
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -1
- package/dist/types/mixins/OxyServices.appData.d.ts +1 -1
- package/dist/types/mixins/OxyServices.applications.d.ts +1 -1
- package/dist/types/mixins/OxyServices.assets.d.ts +1 -1
- package/dist/types/mixins/OxyServices.auth.d.ts +10 -31
- package/dist/types/mixins/OxyServices.contacts.d.ts +1 -1
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -1
- package/dist/types/mixins/OxyServices.features.d.ts +1 -1
- package/dist/types/mixins/OxyServices.fedcm.d.ts +5 -5
- package/dist/types/mixins/OxyServices.language.d.ts +1 -1
- package/dist/types/mixins/OxyServices.location.d.ts +1 -1
- package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -1
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -1
- package/dist/types/mixins/OxyServices.popup.d.ts +18 -120
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -1
- package/dist/types/mixins/OxyServices.redirect.d.ts +13 -174
- package/dist/types/mixins/OxyServices.reputation.d.ts +1 -1
- package/dist/types/mixins/OxyServices.security.d.ts +1 -1
- package/dist/types/mixins/OxyServices.silent.d.ts +131 -0
- package/dist/types/mixins/OxyServices.sso.d.ts +4 -5
- package/dist/types/mixins/OxyServices.topics.d.ts +1 -1
- package/dist/types/mixins/OxyServices.user.d.ts +1 -1
- package/dist/types/mixins/OxyServices.utility.d.ts +3 -8
- package/dist/types/mixins/OxyServices.workspaces.d.ts +1 -1
- package/dist/types/mixins/index.d.ts +3 -3
- package/dist/types/models/interfaces.d.ts +5 -16
- package/dist/types/models/session.d.ts +0 -2
- package/dist/types/server/index.d.ts +18 -0
- package/dist/types/server/rateLimit.d.ts +40 -0
- package/dist/types/shared/utils/debugUtils.d.ts +1 -1
- package/dist/types/utils/authHelpers.d.ts +4 -3
- package/dist/types/utils/coldBoot.d.ts +2 -2
- package/dist/types/utils/fapiAutoDetect.d.ts +1 -1
- package/package.json +25 -3
- package/src/AuthManager.ts +100 -370
- package/src/AuthManagerTypes.ts +5 -9
- package/src/CrossDomainAuth.ts +22 -129
- package/src/HttpService.ts +55 -73
- package/src/OxyServices.base.ts +2 -3
- package/src/OxyServices.ts +9 -11
- package/src/__tests__/authManager.cookiePath.test.ts +19 -17
- package/src/__tests__/authManager.security.test.ts +7 -3
- package/src/__tests__/crossDomainAuth.test.ts +26 -118
- package/src/i18n/index.ts +7 -1
- package/src/i18n/locales/ar-SA.json +18 -2
- package/src/i18n/locales/ca-ES.json +18 -2
- package/src/i18n/locales/de-DE.json +18 -2
- package/src/i18n/locales/en-US.json +17 -3
- package/src/i18n/locales/es-ES.json +16 -2
- package/src/i18n/locales/fr-FR.json +18 -2
- package/src/i18n/locales/it-IT.json +18 -2
- package/src/i18n/locales/ja-JP.json +18 -2
- package/src/i18n/locales/ko-KR.json +18 -2
- package/src/i18n/locales/pt-PT.json +18 -2
- package/src/i18n/locales/zh-CN.json +18 -2
- package/src/index.ts +1 -1
- package/src/mixins/OxyServices.auth.ts +23 -75
- package/src/mixins/OxyServices.fedcm.ts +10 -12
- package/src/mixins/OxyServices.redirect.ts +82 -371
- package/src/mixins/OxyServices.silent.ts +272 -0
- package/src/mixins/OxyServices.sso.ts +5 -6
- package/src/mixins/OxyServices.utility.ts +9 -22
- package/src/mixins/__tests__/appData.test.ts +1 -1
- package/src/mixins/__tests__/onTokensChanged.test.ts +1 -1
- package/src/mixins/__tests__/reputation.test.ts +1 -1
- package/src/mixins/__tests__/serviceAuth.test.ts +7 -5
- package/src/mixins/__tests__/silent.test.ts +102 -0
- package/src/mixins/__tests__/verifyChallenge.test.ts +9 -14
- package/src/mixins/index.ts +6 -8
- package/src/models/interfaces.ts +5 -16
- package/src/models/session.ts +1 -3
- package/src/server/index.ts +19 -0
- package/src/server/rateLimit.ts +170 -0
- package/src/shared/utils/debugUtils.ts +1 -1
- package/src/utils/accountUtils.ts +4 -4
- package/src/utils/authHelpers.ts +23 -15
- package/src/utils/coldBoot.ts +4 -4
- package/src/utils/fapiAutoDetect.ts +1 -1
- package/src/mixins/OxyServices.popup.ts +0 -631
- 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
|
|
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
|
|
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
|
|
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
|
|
2
|
-
import type { SessionLoginResponse } from
|
|
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
|
-
|
|
10
|
+
/** Legacy option. Signup also fails closed. */
|
|
11
|
+
mode?: "login" | "signup";
|
|
8
12
|
/**
|
|
9
|
-
* A popup window handle
|
|
10
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
143
|
-
*
|
|
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
|
|
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
|
|
9
|
+
* Redirect-based authentication without bearer tokens in URLs.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
42
|
-
*
|
|
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
|
|
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
|
|
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
|
|
252
|
+
setTokens(accessToken: string): void;
|
|
253
253
|
clearTokens(): void;
|
|
254
254
|
onTokensChanged(listener: (accessToken: string | null) => void): () => void;
|
|
255
255
|
_cachedUserId: string | null | undefined;
|