@oxyhq/core 5.5.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/HttpService.js +7 -4
- package/dist/cjs/OxyServices.base.js +7 -102
- package/dist/cjs/boot/coldBootV2.js +350 -0
- package/dist/cjs/boot/deviceBootReturn.js +152 -0
- package/dist/cjs/crypto/keyManager.js +95 -0
- package/dist/cjs/i18n/locales/en-US.json +13 -1
- package/dist/cjs/i18n/locales/es-ES.json +13 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +13 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +13 -1
- package/dist/cjs/index.js +54 -45
- package/dist/cjs/mixins/OxyServices.accounts.js +13 -26
- package/dist/cjs/mixins/OxyServices.auth.js +66 -201
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +119 -0
- package/dist/cjs/mixins/index.js +8 -17
- package/dist/cjs/server/index.js +2 -2
- package/dist/cjs/session/SessionClient.js +40 -1
- package/dist/cjs/session/authStateStore.js +284 -0
- package/dist/cjs/session/refresh.js +264 -0
- package/dist/cjs/shared/utils/debugUtils.js +3 -3
- package/dist/cjs/utils/accountUtils.js +1 -55
- package/dist/cjs/utils/authWebUrl.js +10 -39
- package/dist/cjs/utils/coldBoot.js +10 -8
- package/dist/cjs/utils/platform.js +19 -0
- package/dist/cjs/utils/registrableApex.js +49 -0
- package/dist/cjs/utils/ssoBounce.js +15 -362
- package/dist/cjs/utils/validationUtils.js +57 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +7 -4
- package/dist/esm/OxyServices.base.js +7 -102
- package/dist/esm/boot/coldBootV2.js +344 -0
- package/dist/esm/boot/deviceBootReturn.js +146 -0
- package/dist/esm/crypto/keyManager.js +95 -0
- package/dist/esm/i18n/locales/en-US.json +13 -1
- package/dist/esm/i18n/locales/es-ES.json +13 -1
- package/dist/esm/i18n/locales/locales/en-US.json +13 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +13 -1
- package/dist/esm/index.js +34 -17
- package/dist/esm/mixins/OxyServices.accounts.js +13 -26
- package/dist/esm/mixins/OxyServices.auth.js +66 -201
- package/dist/esm/mixins/OxyServices.deviceBoot.js +116 -0
- package/dist/esm/mixins/index.js +8 -17
- package/dist/esm/server/index.js +1 -1
- package/dist/esm/session/SessionClient.js +40 -1
- package/dist/esm/session/authStateStore.js +278 -0
- package/dist/esm/session/refresh.js +257 -0
- package/dist/esm/shared/utils/debugUtils.js +3 -3
- package/dist/esm/utils/accountUtils.js +0 -53
- package/dist/esm/utils/authWebUrl.js +9 -37
- package/dist/esm/utils/coldBoot.js +10 -8
- package/dist/esm/utils/platform.js +18 -0
- package/dist/esm/utils/registrableApex.js +46 -0
- package/dist/esm/utils/ssoBounce.js +14 -345
- package/dist/esm/utils/validationUtils.js +56 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +14 -1
- package/dist/types/OxyServices.base.d.ts +0 -52
- package/dist/types/OxyServices.d.ts +0 -25
- package/dist/types/boot/coldBootV2.d.ts +76 -0
- package/dist/types/boot/deviceBootReturn.d.ts +83 -0
- package/dist/types/crypto/keyManager.d.ts +21 -0
- package/dist/types/index.d.ts +14 -21
- package/dist/types/mixins/OxyServices.accounts.d.ts +24 -29
- package/dist/types/mixins/OxyServices.analytics.d.ts +0 -2
- package/dist/types/mixins/OxyServices.appData.d.ts +0 -2
- package/dist/types/mixins/OxyServices.assets.d.ts +0 -2
- package/dist/types/mixins/OxyServices.auth.d.ts +35 -77
- package/dist/types/mixins/OxyServices.civic.d.ts +0 -2
- package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -2
- package/dist/types/mixins/OxyServices.contacts.d.ts +0 -2
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +110 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +0 -2
- package/dist/types/mixins/OxyServices.features.d.ts +0 -2
- package/dist/types/mixins/OxyServices.identity.d.ts +0 -2
- package/dist/types/mixins/OxyServices.language.d.ts +0 -2
- package/dist/types/mixins/OxyServices.links.d.ts +0 -2
- package/dist/types/mixins/OxyServices.location.d.ts +0 -2
- package/dist/types/mixins/OxyServices.nodes.d.ts +0 -2
- package/dist/types/mixins/OxyServices.payment.d.ts +0 -2
- package/dist/types/mixins/OxyServices.privacy.d.ts +0 -2
- package/dist/types/mixins/OxyServices.reputation.d.ts +0 -2
- package/dist/types/mixins/OxyServices.security.d.ts +0 -2
- package/dist/types/mixins/OxyServices.topics.d.ts +0 -2
- package/dist/types/mixins/OxyServices.user.d.ts +0 -2
- package/dist/types/mixins/OxyServices.utility.d.ts +0 -2
- package/dist/types/mixins/index.d.ts +5 -9
- package/dist/types/models/interfaces.d.ts +0 -67
- package/dist/types/models/session.d.ts +4 -5
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/session/SessionClient.d.ts +26 -1
- package/dist/types/session/authStateStore.d.ts +119 -0
- package/dist/types/session/refresh.d.ts +93 -0
- package/dist/types/shared/utils/debugUtils.d.ts +3 -3
- package/dist/types/utils/accountUtils.d.ts +2 -17
- package/dist/types/utils/authWebUrl.d.ts +9 -35
- package/dist/types/utils/coldBoot.d.ts +17 -14
- package/dist/types/utils/platform.d.ts +14 -0
- package/dist/types/utils/registrableApex.d.ts +31 -0
- package/dist/types/utils/ssoBounce.d.ts +14 -280
- package/dist/types/utils/validationUtils.d.ts +15 -0
- package/package.json +2 -2
- package/src/HttpService.ts +20 -4
- package/src/OxyServices.base.ts +7 -112
- package/src/OxyServices.ts +0 -38
- package/src/boot/__tests__/coldBootV2.test.ts +317 -0
- package/src/boot/__tests__/deviceBootReturn.test.ts +158 -0
- package/src/boot/coldBootV2.ts +426 -0
- package/src/boot/deviceBootReturn.ts +195 -0
- package/src/crypto/__tests__/sharedDeviceToken.test.ts +24 -0
- package/src/crypto/keyManager.ts +101 -0
- package/src/i18n/locales/en-US.json +13 -1
- package/src/i18n/locales/es-ES.json +13 -1
- package/src/index.ts +75 -65
- package/src/mixins/OxyServices.accounts.ts +27 -39
- package/src/mixins/OxyServices.auth.ts +78 -253
- package/src/mixins/OxyServices.deviceBoot.ts +146 -0
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +107 -0
- package/src/mixins/__tests__/accounts.test.ts +17 -44
- package/src/mixins/__tests__/passwordSignIn.test.ts +91 -0
- package/src/mixins/index.ts +10 -23
- package/src/models/interfaces.ts +0 -79
- package/src/models/session.ts +4 -5
- package/src/server/index.ts +1 -1
- package/src/session/SessionClient.ts +53 -2
- package/src/session/__tests__/SessionClient.additive.test.ts +92 -0
- package/src/session/__tests__/SessionClient.rest.test.ts +25 -0
- package/src/session/__tests__/SessionClient.state.test.ts +18 -5
- package/src/session/__tests__/authStateStore.test.ts +209 -0
- package/src/session/__tests__/refresh.test.ts +256 -0
- package/src/session/authStateStore.ts +335 -0
- package/src/session/refresh.ts +334 -0
- package/src/shared/utils/debugUtils.ts +3 -3
- package/src/utils/__tests__/authWebUrl.test.ts +5 -40
- package/src/utils/__tests__/registrableApex.test.ts +62 -0
- package/src/utils/__tests__/validationUtils.test.ts +30 -0
- package/src/utils/accountUtils.ts +2 -65
- package/src/utils/authWebUrl.ts +9 -39
- package/src/utils/coldBoot.ts +17 -14
- package/src/utils/platform.ts +21 -0
- package/src/utils/registrableApex.ts +45 -0
- package/src/utils/ssoBounce.ts +14 -393
- package/src/utils/validationUtils.ts +62 -0
- package/dist/cjs/AuthManager.js +0 -1110
- package/dist/cjs/AuthManagerTypes.js +0 -13
- package/dist/cjs/CrossDomainAuth.js +0 -206
- package/dist/cjs/mixins/OxyServices.fedcm.js +0 -823
- package/dist/cjs/mixins/OxyServices.redirect.js +0 -95
- package/dist/cjs/mixins/OxyServices.silent.js +0 -204
- package/dist/cjs/mixins/OxyServices.sso.js +0 -208
- package/dist/cjs/utils/fapiAutoDetect.js +0 -99
- package/dist/cjs/utils/ssoEstablish.js +0 -110
- package/dist/cjs/utils/ssoReturn.js +0 -275
- package/dist/esm/AuthManager.js +0 -1105
- package/dist/esm/AuthManagerTypes.js +0 -12
- package/dist/esm/CrossDomainAuth.js +0 -201
- package/dist/esm/mixins/OxyServices.fedcm.js +0 -821
- package/dist/esm/mixins/OxyServices.redirect.js +0 -92
- package/dist/esm/mixins/OxyServices.silent.js +0 -202
- package/dist/esm/mixins/OxyServices.sso.js +0 -204
- package/dist/esm/utils/fapiAutoDetect.js +0 -95
- package/dist/esm/utils/ssoEstablish.js +0 -107
- package/dist/esm/utils/ssoReturn.js +0 -271
- package/dist/types/AuthManager.d.ts +0 -380
- package/dist/types/AuthManagerTypes.d.ts +0 -81
- package/dist/types/CrossDomainAuth.d.ts +0 -164
- package/dist/types/mixins/OxyServices.fedcm.d.ts +0 -331
- package/dist/types/mixins/OxyServices.redirect.d.ts +0 -92
- package/dist/types/mixins/OxyServices.silent.d.ts +0 -132
- package/dist/types/mixins/OxyServices.sso.d.ts +0 -138
- package/dist/types/utils/fapiAutoDetect.d.ts +0 -56
- package/dist/types/utils/ssoEstablish.d.ts +0 -85
- package/dist/types/utils/ssoReturn.d.ts +0 -156
- package/src/AuthManager.ts +0 -1269
- package/src/AuthManagerTypes.ts +0 -86
- package/src/CrossDomainAuth.ts +0 -243
- package/src/__tests__/authManager.cookiePath.test.ts +0 -390
- package/src/__tests__/authManager.security.test.ts +0 -377
- package/src/__tests__/crossDomainAuth.test.ts +0 -116
- package/src/__tests__/establishDeviceRefreshSlot.test.ts +0 -221
- package/src/mixins/OxyServices.fedcm.ts +0 -1026
- package/src/mixins/OxyServices.redirect.ts +0 -122
- package/src/mixins/OxyServices.silent.ts +0 -272
- package/src/mixins/OxyServices.sso.ts +0 -261
- package/src/mixins/__tests__/constructorAuthWebUrl.test.ts +0 -85
- package/src/mixins/__tests__/fedcm.test.ts +0 -667
- package/src/mixins/__tests__/sessionBaseUrl.test.ts +0 -61
- package/src/mixins/__tests__/silent.test.ts +0 -102
- package/src/mixins/__tests__/sso.test.ts +0 -228
- package/src/utils/__tests__/consumeSsoReturn.test.ts +0 -816
- package/src/utils/__tests__/fapiAutoDetect.test.ts +0 -183
- package/src/utils/__tests__/ssoBounce.test.ts +0 -219
- package/src/utils/__tests__/ssoEstablish.test.ts +0 -204
- package/src/utils/__tests__/ssoReturn.test.ts +0 -276
- package/src/utils/fapiAutoDetect.ts +0 -91
- package/src/utils/ssoEstablish.ts +0 -174
- package/src/utils/ssoReturn.ts +0 -389
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Central Cross-Domain SSO (opaque-code) Mixin
|
|
3
|
-
*
|
|
4
|
-
* Implements the Relying-Party half of TRUE central cross-domain SSO
|
|
5
|
-
* (Google/Meta/Clerk style). The central IdP at `auth.oxy.so` owns the session;
|
|
6
|
-
* an RP bounces a top-level redirect (prompt=none) to `auth.oxy.so/sso`, which
|
|
7
|
-
* returns an OPAQUE single-use code in the redirect fragment. The RP then
|
|
8
|
-
* exchanges that code here for the real session.
|
|
9
|
-
*
|
|
10
|
-
* Security properties:
|
|
11
|
-
* - NO token/JWT ever travels in a URL — only the opaque code does. The real
|
|
12
|
-
* `accessToken` is delivered exclusively in this exchange response body.
|
|
13
|
-
* - The exchange is a CORS POST with NO credentials/cookies — the opaque code
|
|
14
|
-
* is the only bearer of authority, and the central store burns it atomically
|
|
15
|
-
* (single-use). Sending no cookies keeps the request a clean, ambient-
|
|
16
|
-
* authority-free bearer exchange that the central `POST /sso/exchange`
|
|
17
|
-
* endpoint validates by `Origin` against the code's bound `clientOrigin`.
|
|
18
|
-
* - The code is minted server-side bound to the RP origin and expires in
|
|
19
|
-
* seconds, so a leaked code is useless cross-origin and short-lived.
|
|
20
|
-
*
|
|
21
|
-
* On success the mixin plants the returned access token via
|
|
22
|
-
* `httpService.setTokens(...)` — mirroring `exchangeIdTokenForSession` /
|
|
23
|
-
* `verifyChallenge` — so callers do NOT need to plant tokens manually.
|
|
24
|
-
*/
|
|
25
|
-
import type { OxyServicesBase } from '../OxyServices.base';
|
|
26
|
-
import type { SessionLoginResponse } from '../models/session';
|
|
27
|
-
/**
|
|
28
|
-
* Generate a cryptographically secure state value for the SSO bounce.
|
|
29
|
-
*
|
|
30
|
-
* Exposed as a module-level helper (in addition to the instance method below)
|
|
31
|
-
* so consumers that do not yet hold an `OxyServices` instance can still mint a
|
|
32
|
-
* bounce state. Uses `crypto.randomUUID` with a `getRandomValues` fallback.
|
|
33
|
-
*/
|
|
34
|
-
export declare function generateSsoState(): string;
|
|
35
|
-
export declare function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Base: T): {
|
|
36
|
-
new (...args: any[]): {
|
|
37
|
-
/**
|
|
38
|
-
* Generate cryptographically secure state for the SSO bounce (CSRF
|
|
39
|
-
* protection). Delegates to the module-level {@link generateSsoState}
|
|
40
|
-
* helper, which uses `crypto.randomUUID` when available and falls back to
|
|
41
|
-
* `crypto.getRandomValues`.
|
|
42
|
-
*/
|
|
43
|
-
generateSsoState(): string;
|
|
44
|
-
/**
|
|
45
|
-
* Exchange an opaque single-use SSO code for the real Oxy session.
|
|
46
|
-
*
|
|
47
|
-
* POSTs `{ code }` to `${getSessionBaseUrl()}/sso/exchange` as a CORS
|
|
48
|
-
* request with NO credentials/cookies. On success the returned access token
|
|
49
|
-
* is planted via `httpService.setTokens(...)` (matching
|
|
50
|
-
* `exchangeIdTokenForSession` / `verifyChallenge`), so callers do not need
|
|
51
|
-
* to plant tokens manually.
|
|
52
|
-
*
|
|
53
|
-
* @param code - The opaque single-use code delivered in the SSO return
|
|
54
|
-
* fragment (see {@link parseSsoReturnFragment}). The central store burns
|
|
55
|
-
* it atomically on exchange.
|
|
56
|
-
* @param state - The state value returned alongside the code. In browsers,
|
|
57
|
-
* when an SSO bounce state is still stored for the current origin, this
|
|
58
|
-
* must match before any token-committing exchange is attempted.
|
|
59
|
-
* @returns The resolved {@link SessionLoginResponse}.
|
|
60
|
-
*/
|
|
61
|
-
exchangeSsoCode(code: string, state?: string): Promise<SessionLoginResponse>;
|
|
62
|
-
/**
|
|
63
|
-
* Mint a server-formed `/sso/establish` URL for the caller's OWN session,
|
|
64
|
-
* bound to an approved RP `origin`.
|
|
65
|
-
*
|
|
66
|
-
* Bearer-authenticated (the session id is taken from the caller's own
|
|
67
|
-
* bearer, server-side — never from any argument). The server validates that
|
|
68
|
-
* `origin` is an approved client origin (and matches the request `Origin`),
|
|
69
|
-
* derives the per-apex IdP host (`auth.<apex>`), mints a short-lived HS256
|
|
70
|
-
* establish-token, and returns a fully-formed
|
|
71
|
-
* `https://<auth-host>/sso/establish?et=…&return_to=<origin>/__oxy/sso-callback&state=<state>`.
|
|
72
|
-
*
|
|
73
|
-
* Used AFTER a web device-flow claim to plant the durable first-party
|
|
74
|
-
* `fedcm_session` cookie so a reload can re-mint a token (see
|
|
75
|
-
* {@link establishIdpSessionAfterClaim}). Cache-free (a POST is never
|
|
76
|
-
* cached, but `cache: false` is explicit).
|
|
77
|
-
*
|
|
78
|
-
* @param origin - The RP origin (`window.location.origin`) to establish for.
|
|
79
|
-
* @param state - The CSRF state echoed back in the callback fragment; the
|
|
80
|
-
* caller persists the SAME value under `ssoStateKey(origin)` so the
|
|
81
|
-
* post-bounce `sso-return` step validates it.
|
|
82
|
-
*/
|
|
83
|
-
requestSsoEstablishUrl(origin: string, state: string): Promise<{
|
|
84
|
-
establishUrl: string;
|
|
85
|
-
}>;
|
|
86
|
-
httpService: import("../HttpService").HttpService;
|
|
87
|
-
cloudURL: string;
|
|
88
|
-
config: import("../OxyServices.base").OxyConfig;
|
|
89
|
-
__resetTokensForTests(): void;
|
|
90
|
-
makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
|
|
91
|
-
getBaseURL(): string;
|
|
92
|
-
getSessionBaseUrl(): string;
|
|
93
|
-
getClient(): import("../HttpService").HttpService;
|
|
94
|
-
createLinkedClient(config: import("../OxyServices.base").OxyConfig): import("..").LinkedHttpClient;
|
|
95
|
-
getMetrics(): {
|
|
96
|
-
totalRequests: number;
|
|
97
|
-
successfulRequests: number;
|
|
98
|
-
failedRequests: number;
|
|
99
|
-
cacheHits: number;
|
|
100
|
-
cacheMisses: number;
|
|
101
|
-
averageResponseTime: number;
|
|
102
|
-
};
|
|
103
|
-
clearCache(): void;
|
|
104
|
-
clearCacheEntry(key: string): void;
|
|
105
|
-
clearCacheByPrefix(prefix: string): number;
|
|
106
|
-
getCacheStats(): {
|
|
107
|
-
size: number;
|
|
108
|
-
hits: number;
|
|
109
|
-
misses: number;
|
|
110
|
-
hitRate: number;
|
|
111
|
-
};
|
|
112
|
-
getCloudURL(): string;
|
|
113
|
-
setTokens(accessToken: string): void;
|
|
114
|
-
clearTokens(): void;
|
|
115
|
-
onTokensChanged(listener: (accessToken: string | null) => void): () => void;
|
|
116
|
-
_cachedUserId: string | null | undefined;
|
|
117
|
-
_cachedAccessToken: string | null;
|
|
118
|
-
getCurrentUserId(): string | null;
|
|
119
|
-
hasValidToken(): boolean;
|
|
120
|
-
getAccessToken(): string | null;
|
|
121
|
-
establishDeviceRefreshSlot(): Promise<number | null>;
|
|
122
|
-
getAccessTokenExpiry(): number | null;
|
|
123
|
-
waitForAuth(timeoutMs?: number): Promise<boolean>;
|
|
124
|
-
withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
|
|
125
|
-
maxRetries?: number;
|
|
126
|
-
retryDelay?: number;
|
|
127
|
-
authTimeoutMs?: number;
|
|
128
|
-
}): Promise<T_1>;
|
|
129
|
-
validate(): Promise<boolean>;
|
|
130
|
-
handleError(error: unknown): Error;
|
|
131
|
-
healthCheck(): Promise<{
|
|
132
|
-
status: string;
|
|
133
|
-
users?: number;
|
|
134
|
-
timestamp?: string;
|
|
135
|
-
[key: string]: any;
|
|
136
|
-
}>;
|
|
137
|
-
};
|
|
138
|
-
} & T;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-detect the FAPI (IdP) URL from the current browser hostname.
|
|
3
|
-
*
|
|
4
|
-
* This is the canonical cross-domain IdP-resolution primitive for the Oxy
|
|
5
|
-
* ecosystem. Both candidate cross-domain SSO designs derive `auth.<rp-apex>`
|
|
6
|
-
* through this helper; do not fork it.
|
|
7
|
-
*
|
|
8
|
-
* Clerk-style multi-domain SSO depends on the IdP being reachable on a
|
|
9
|
-
* subdomain of the RP's own apex (e.g. `auth.mention.earth` CNAMEd to the
|
|
10
|
-
* central Oxy IdP). That way every FedCM endpoint, the session cookie,
|
|
11
|
-
* and any redirect target are same-site with the RP — the only way
|
|
12
|
-
* to get first-party cookies in Safari ITP and Firefox Total Cookie
|
|
13
|
-
* Protection.
|
|
14
|
-
*
|
|
15
|
-
* This helper computes `https://auth.<rp-apex>` from
|
|
16
|
-
* `window.location.hostname` so a consuming app doesn't have to pass
|
|
17
|
-
* `authWebUrl` explicitly. Returns `undefined` for environments where
|
|
18
|
-
* auto-detection would be wrong:
|
|
19
|
-
*
|
|
20
|
-
* - SSR / non-browser (no `window`).
|
|
21
|
-
* - `localhost`, `127.0.0.1`, IPv4/IPv6 literals.
|
|
22
|
-
* - Hostnames with fewer than two labels.
|
|
23
|
-
* - Hostnames where a registrable domain cannot be determined from the
|
|
24
|
-
* Public Suffix List, including private hosted suffixes such as
|
|
25
|
-
* `github.io`, `pages.dev`, and `netlify.app`.
|
|
26
|
-
*
|
|
27
|
-
* When the page is already loaded ON the IdP itself (`auth.<anything>`),
|
|
28
|
-
* the helper returns the current origin so the SDK keeps everything
|
|
29
|
-
* same-origin instead of hopping to a different IdP host.
|
|
30
|
-
*
|
|
31
|
-
* The IdP backend independently derives `iss`, `provider_urls`, and the
|
|
32
|
-
* `fedcm.json` icon URLs from the request host
|
|
33
|
-
* (`packages/auth/server/index.ts`), so an honest CNAME pair is all that
|
|
34
|
-
* is required for end-to-end FedCM correctness — no per-RP config.
|
|
35
|
-
*/
|
|
36
|
-
/**
|
|
37
|
-
* Compute the bare registrable apex (eTLD+1) of a hostname using the Public
|
|
38
|
-
* Suffix List, including private hosted suffixes.
|
|
39
|
-
*
|
|
40
|
-
* This is the pure host-handling kernel shared by {@link autoDetectAuthWebUrl}
|
|
41
|
-
* and the IdP worker — it performs NO protocol handling, NO `auth.` prefixing,
|
|
42
|
-
* and builds NO URL. It only answers "what is the registrable domain of this
|
|
43
|
-
* host, or is that undefinable?".
|
|
44
|
-
*
|
|
45
|
-
* Returns `null` (apex undefinable) for:
|
|
46
|
-
* - empty input;
|
|
47
|
-
* - IPv4 literals (`192.168.1.10`);
|
|
48
|
-
* - IPv6 literals or any host carrying a port (`[::1]`, anything with `:`);
|
|
49
|
-
* - single-label hosts (`intranet`, `localhost`);
|
|
50
|
-
* - public suffixes without a registrable label (e.g. `co.uk`, `github.io`).
|
|
51
|
-
*
|
|
52
|
-
* @param hostname - A bare hostname (no scheme), e.g. `www.mention.earth`.
|
|
53
|
-
* @returns The eTLD+1 (`mention.earth`), or `null` when undefinable.
|
|
54
|
-
*/
|
|
55
|
-
export declare function registrableApex(hostname: string): string | null;
|
|
56
|
-
export declare function autoDetectAuthWebUrl(location?: Pick<Location, 'hostname' | 'protocol'> | undefined): string | undefined;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post-claim durable-session establish hop (web device-flow / "Sign in with
|
|
3
|
-
* Oxy" QR).
|
|
4
|
-
*
|
|
5
|
-
* A WEB device-flow claim (`claimSessionByToken`) plants only IN-MEMORY tokens:
|
|
6
|
-
* unlike a redirect/FedCM/silent sign-in, it never causes the IdP to plant a
|
|
7
|
-
* `fedcm_session` cookie. So a reload has nothing to restore from — the
|
|
8
|
-
* silent-iframe and `/sso` paths find no IdP session and the session is lost.
|
|
9
|
-
*
|
|
10
|
-
* This primitive closes that gap. AFTER the claim has committed and the session
|
|
11
|
-
* has been durably persisted, it performs ONE top-level establish hop through
|
|
12
|
-
* the RP's own per-apex IdP host (`auth.<rp-apex>`), reusing the EXISTING
|
|
13
|
-
* `/sso/establish` endpoint: the server mints a short-lived, host+audience-bound
|
|
14
|
-
* establish-token and returns a fully-formed establish URL; navigating to it
|
|
15
|
-
* plants the durable first-party `fedcm_session` cookie and bounces back to the
|
|
16
|
-
* RP callback with an opaque code the standard `sso-return` cold-boot step
|
|
17
|
-
* exchanges.
|
|
18
|
-
*
|
|
19
|
-
* It reuses the SAME per-origin `sessionStorage` bounce contract
|
|
20
|
-
* (`ssoStateKey` / `ssoGuardKey` / `ssoDestKey`) that {@link buildSsoBounceUrl}
|
|
21
|
-
* primes for the terminal `/sso` bounce, so the post-bounce `sso-return` step
|
|
22
|
-
* (`consumeSsoReturn`) validates the CSRF `state`, exchanges the code, and
|
|
23
|
-
* restores the user's real destination with no extra wiring.
|
|
24
|
-
*
|
|
25
|
-
* Contract:
|
|
26
|
-
* - WEB only — off-web / native it is a no-op returning `false`.
|
|
27
|
-
* - NEVER fires while sitting on the central IdP origin (that would loop the
|
|
28
|
-
* IdP against itself).
|
|
29
|
-
* - Bounce state is persisted ONLY after the establish-URL request succeeds, so
|
|
30
|
-
* a failed request leaves no stale state behind.
|
|
31
|
-
* - SINGLE attempt: the caller invokes this exactly once per successful claim.
|
|
32
|
-
* On ANY failure it does NOT navigate and returns `false`, leaving the
|
|
33
|
-
* committed in-memory session exactly as-is (the user is no worse off than
|
|
34
|
-
* before this hop existed).
|
|
35
|
-
* - Total: never throws. Failures are reported via {@link deps.onError} only.
|
|
36
|
-
*/
|
|
37
|
-
/**
|
|
38
|
-
* The minimal SDK surface this hop needs: mint a server-formed establish URL
|
|
39
|
-
* bound to the caller's own session for an approved RP origin. Structural so the
|
|
40
|
-
* primitive is unit-testable with a stub and never imports the full client.
|
|
41
|
-
*/
|
|
42
|
-
export interface SsoEstablishClient {
|
|
43
|
-
requestSsoEstablishUrl(origin: string, state: string): Promise<{
|
|
44
|
-
establishUrl: string;
|
|
45
|
-
}>;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Injectable web seams for {@link establishIdpSessionAfterClaim}. Every seam is
|
|
49
|
-
* overridable so the primitive is fully unit-testable with fakes and so native
|
|
50
|
-
* callers rely on the defaults (which resolve to `window.*` only when a browser
|
|
51
|
-
* is present). Defaults are evaluated lazily inside the function so importing
|
|
52
|
-
* this module never touches `window`.
|
|
53
|
-
*/
|
|
54
|
-
export interface EstablishAfterClaimDeps {
|
|
55
|
-
/** Per-tab SSO bounce store. Default: `window.sessionStorage`. */
|
|
56
|
-
storage?: Pick<Storage, 'getItem' | 'setItem'>;
|
|
57
|
-
/** The current location. Default: `window.location`. */
|
|
58
|
-
location?: Pick<Location, 'origin' | 'href'>;
|
|
59
|
-
/** Top-level navigation seam. Default: {@link ssoNavigate} (`location.assign`). */
|
|
60
|
-
navigate?: (url: string) => void;
|
|
61
|
-
/**
|
|
62
|
-
* Whether the current environment is a web browser with usable
|
|
63
|
-
* `sessionStorage`. Default: `typeof window !== 'undefined' && typeof
|
|
64
|
-
* window.sessionStorage !== 'undefined'`.
|
|
65
|
-
*/
|
|
66
|
-
isWeb?: () => boolean;
|
|
67
|
-
/** CSRF state generator. Default: {@link generateSsoState}. */
|
|
68
|
-
generateState?: () => string;
|
|
69
|
-
/** Epoch-ms clock for the bounce guard. Default: `Date.now`. */
|
|
70
|
-
now?: () => number;
|
|
71
|
-
/**
|
|
72
|
-
* Optional debug hook invoked with the thrown error when the establish
|
|
73
|
-
* request (or state persistence) fails. NEVER rethrown. Default: no-op.
|
|
74
|
-
*/
|
|
75
|
-
onError?: (error: unknown) => void;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Perform the post-claim establish hop. Returns `true` when a navigation to the
|
|
79
|
-
* establish URL was initiated (the document is being torn down and replaced by
|
|
80
|
-
* the IdP), `false` on every no-op / failure path.
|
|
81
|
-
*
|
|
82
|
-
* @param client - The exchange surface (`oxyServices.requestSsoEstablishUrl`).
|
|
83
|
-
* @param deps - Injectable web seams; see {@link EstablishAfterClaimDeps}.
|
|
84
|
-
*/
|
|
85
|
-
export declare function establishIdpSessionAfterClaim(client: SsoEstablishClient, deps?: EstablishAfterClaimDeps): Promise<boolean>;
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse the SSO return fragment delivered by the central IdP.
|
|
3
|
-
*
|
|
4
|
-
* After a top-level redirect bounce to `auth.oxy.so/sso` (prompt=none), the
|
|
5
|
-
* central IdP returns the Relying Party to its `redirect_uri` with the result
|
|
6
|
-
* encoded in the URL fragment (the `#…` part). The fragment is used — not a
|
|
7
|
-
* query string — so the opaque single-use code never reaches a server access
|
|
8
|
-
* log, a `Referer` header, or browser history in a recoverable form.
|
|
9
|
-
*
|
|
10
|
-
* Three outcomes are possible:
|
|
11
|
-
* - `#oxy_sso=ok&code=<opaque>&state=<state>` — the IdP had a session; the RP
|
|
12
|
-
* exchanges `code` (via `oxy.exchangeSsoCode`) for the real session. NO
|
|
13
|
-
* token/JWT ever appears in the URL — only the opaque code.
|
|
14
|
-
* - `#oxy_sso=none&state=<state>` — the IdP had no session (prompt=none, user
|
|
15
|
-
* not signed in centrally). The RP shows its own signed-out UI.
|
|
16
|
-
* - `#oxy_sso=error&state=<state>` — the bounce failed. The RP recovers.
|
|
17
|
-
*
|
|
18
|
-
* This parser is pure and defensive: it never throws, and `kind` is strictly
|
|
19
|
-
* one of `'ok' | 'none' | 'error'`. It returns `null` when the fragment is not
|
|
20
|
-
* an oxy_sso fragment at all (i.e. `oxy_sso` is absent or an unrecognised
|
|
21
|
-
* value), so the caller can ignore unrelated fragments without special-casing.
|
|
22
|
-
*/
|
|
23
|
-
import type { SessionLoginResponse } from '../models/session';
|
|
24
|
-
/**
|
|
25
|
-
* The recognised outcomes of an SSO bounce.
|
|
26
|
-
*/
|
|
27
|
-
export type SsoReturnKind = 'ok' | 'none' | 'error';
|
|
28
|
-
/**
|
|
29
|
-
* The parsed result of an SSO return fragment.
|
|
30
|
-
*
|
|
31
|
-
* `code` is present only for `kind: 'ok'`. `state` echoes the CSRF state the RP
|
|
32
|
-
* generated for the bounce (when the IdP round-tripped it). `reason` is present
|
|
33
|
-
* only for a NON-`ok` outcome when the IdP supplied one.
|
|
34
|
-
*/
|
|
35
|
-
export interface SsoReturnResult {
|
|
36
|
-
kind: SsoReturnKind;
|
|
37
|
-
code?: string;
|
|
38
|
-
state?: string;
|
|
39
|
-
/**
|
|
40
|
-
* Machine-readable reason accompanying a NON-`ok` outcome, when the central
|
|
41
|
-
* IdP supplies one (e.g. `no_cookie` | `stale_session` | `no_grant` |
|
|
42
|
-
* `no_grant_establish` on a `none` bounce). Purely informational: it lets a
|
|
43
|
-
* Relying Party surface WHY a silent probe returned no session (e.g. to brand
|
|
44
|
-
* a "sign in" screen) without re-deriving it. Absent on `ok`, and whenever the
|
|
45
|
-
* IdP did not include a `reason` param.
|
|
46
|
-
*/
|
|
47
|
-
reason?: string;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Parse an SSO return fragment.
|
|
51
|
-
*
|
|
52
|
-
* @param hash - The URL fragment, with or without the leading `#`
|
|
53
|
-
* (e.g. `location.hash`). May be `undefined`/empty.
|
|
54
|
-
* @returns The parsed result when `hash` is a recognised oxy_sso fragment,
|
|
55
|
-
* otherwise `null`. Never throws.
|
|
56
|
-
*/
|
|
57
|
-
export declare function parseSsoReturnFragment(hash: string | undefined | null): SsoReturnResult | null;
|
|
58
|
-
/**
|
|
59
|
-
* Injectable dependencies for {@link consumeSsoReturn}.
|
|
60
|
-
*
|
|
61
|
-
* Every web seam (storage, location, history, web-detection) is injectable so
|
|
62
|
-
* the function is fully unit-testable with fakes and so SSR / native callers
|
|
63
|
-
* can supply their own (or rely on the defaults, which resolve to `window.*`
|
|
64
|
-
* only when a browser is present). Defaults are evaluated lazily inside
|
|
65
|
-
* `consumeSsoReturn` so importing this module never touches `window`.
|
|
66
|
-
*/
|
|
67
|
-
export interface ConsumeSsoReturnDeps {
|
|
68
|
-
/** Per-tab SSO state store. Default: `window.sessionStorage`. */
|
|
69
|
-
storage?: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
70
|
-
/** The current location. Default: `window.location`. */
|
|
71
|
-
location?: Pick<Location, 'hash' | 'origin' | 'pathname' | 'search'>;
|
|
72
|
-
/** History API for fragment stripping / dest restore. Default: `window.history`. */
|
|
73
|
-
history?: Pick<History, 'replaceState'>;
|
|
74
|
-
/**
|
|
75
|
-
* Whether the current environment is a web browser with usable
|
|
76
|
-
* `sessionStorage`. Default: `typeof window !== 'undefined' && typeof
|
|
77
|
-
* window.sessionStorage !== 'undefined'`.
|
|
78
|
-
*/
|
|
79
|
-
isWeb?: () => boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Optional debug hook invoked with the thrown error when the code exchange
|
|
82
|
-
* fails. NEVER rethrown — `consumeSsoReturn` is total. Default: no-op.
|
|
83
|
-
*/
|
|
84
|
-
onExchangeError?: (error: unknown) => void;
|
|
85
|
-
/**
|
|
86
|
-
* Notify URL-driven routers (Expo Router / React Navigation web) that the
|
|
87
|
-
* location changed via `history.replaceState`, which does NOT itself emit
|
|
88
|
-
* `popstate`. Default: dispatch a real `PopStateEvent` on `window` when
|
|
89
|
-
* present; no-op off-web. Called ONLY after a successful same-origin
|
|
90
|
-
* dest restore on the `ok` path (never when the dest is rejected/absent).
|
|
91
|
-
* NEVER throws.
|
|
92
|
-
*/
|
|
93
|
-
dispatchPopState?: () => void;
|
|
94
|
-
/**
|
|
95
|
-
* Hard, full-document navigation used to leave the internal callback path on
|
|
96
|
-
* every NON-`ok` outcome (`none`/`error`, state-mismatch, missing code,
|
|
97
|
-
* failed exchange, missing sessionId). A SOFT `history.replaceState` +
|
|
98
|
-
* synthetic `popstate` does NOT reliably make Expo Router / TanStack Router
|
|
99
|
-
* re-resolve away from the 404 they have already rendered for the
|
|
100
|
-
* unregistered callback route — so for these outcomes (where there is no
|
|
101
|
-
* in-memory session to preserve) a full navigation is both safe and
|
|
102
|
-
* guaranteed to clear the 404. Default: `window.location.replace(url)` when
|
|
103
|
-
* present; feature-detected end to end so it never throws off-web.
|
|
104
|
-
*/
|
|
105
|
-
hardRedirect?: (url: string) => void;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Consume an SSO return: the commit-free, security-critical kernel of the
|
|
109
|
-
* cross-domain SSO `sso-return` cold-boot step.
|
|
110
|
-
*
|
|
111
|
-
* This performs the CSRF/fragment/exchange/dest-restore/loop-breaker sequence
|
|
112
|
-
* and RETURNS the exchanged session (or `null`). It deliberately does NOT
|
|
113
|
-
* commit any UI/auth state — each provider commits its own way AROUND this
|
|
114
|
-
* (e.g. `@oxyhq/services` `OxyContext` calls its `handleWebSSOSession`,
|
|
115
|
-
* `@oxyhq/auth` `WebOxyProvider` updates its React state). Hoisting the kernel
|
|
116
|
-
* here keeps the two providers byte-for-byte identical on the parts that matter
|
|
117
|
-
* for security (state validation, fragment stripping order, loop prevention).
|
|
118
|
-
*
|
|
119
|
-
* Security/loop invariants (preserved exactly from both former copies):
|
|
120
|
-
* - The fragment is stripped via `history.replaceState` FIRST — before the
|
|
121
|
-
* exchange — so the opaque code never lingers in the URL, browser history,
|
|
122
|
-
* or a `Referer` header even if a later step throws.
|
|
123
|
-
* - `state` must match (CSRF). A mismatch or a missing code sets the
|
|
124
|
-
* NO_SESSION flag so `sso-bounce` is disabled (no rebounce loop).
|
|
125
|
-
* - `none`/`error` outcomes set BOTH the NO_SESSION flag and the
|
|
126
|
-
* outcome-independent attempted-flag (the load2 half of the loop proof).
|
|
127
|
-
* - A throwing exchange is caught, reported via `onExchangeError`, and
|
|
128
|
-
* treated exactly like "no session" (never loops, never rethrows).
|
|
129
|
-
* - On EVERY consumed outcome (ok, none, error, state-mismatch, no-code,
|
|
130
|
-
* failed-exchange, no-sessionId) — not just ok — if the page landed on
|
|
131
|
-
* {@link SSO_CALLBACK_PATH}, the user is taken to a same-origin TARGET so
|
|
132
|
-
* they are never stranded on the internal callback path (which is an
|
|
133
|
-
* unregistered route in every consumer router → a hard 404). The target is
|
|
134
|
-
* the stored DEST when it parses as same-origin (an attacker-planted
|
|
135
|
-
* cross-origin / protocol-relative dest is rejected), ELSE the app root
|
|
136
|
-
* (`origin + '/'`). The DEST key is removed unconditionally.
|
|
137
|
-
* - For the `ok` outcome the target is applied via a SOFT
|
|
138
|
-
* `history.replaceState` + synthetic `popstate` so the freshly exchanged
|
|
139
|
-
* in-memory session the provider is about to commit is preserved (no
|
|
140
|
-
* reload). `popstate` is dispatched only on the `ok` same-origin restore.
|
|
141
|
-
* - For every NON-`ok` outcome there is no in-memory session to preserve, and
|
|
142
|
-
* the consumer router has ALREADY synchronously rendered its 404 for the
|
|
143
|
-
* unregistered callback route — a soft replaceState+popstate does not
|
|
144
|
-
* reliably make it re-resolve. So these outcomes perform a HARD
|
|
145
|
-
* full-document navigation to the target (`hardRedirect`), which is both
|
|
146
|
-
* safe (nothing to lose) and guaranteed to clear the 404 in every router.
|
|
147
|
-
*
|
|
148
|
-
* Total: this function NEVER throws. Off-web it is a no-op returning `null`.
|
|
149
|
-
*
|
|
150
|
-
* @param oxy - The exchange surface (`oxyServices.exchangeSsoCode`).
|
|
151
|
-
* @param deps - Injectable web seams; see {@link ConsumeSsoReturnDeps}.
|
|
152
|
-
* @returns The exchanged session on success, otherwise `null`.
|
|
153
|
-
*/
|
|
154
|
-
export declare function consumeSsoReturn(oxy: {
|
|
155
|
-
exchangeSsoCode: (code: string, state?: string) => Promise<SessionLoginResponse>;
|
|
156
|
-
}, deps?: ConsumeSsoReturnDeps): Promise<SessionLoginResponse | null>;
|