@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
|
@@ -2,20 +2,6 @@ import type { UserNameResponse } from '@oxyhq/contracts';
|
|
|
2
2
|
export interface OxyConfig {
|
|
3
3
|
baseURL: string;
|
|
4
4
|
cloudURL?: string;
|
|
5
|
-
/**
|
|
6
|
-
* Base URL the SDK's first-party session/refresh calls target.
|
|
7
|
-
*
|
|
8
|
-
* Per the 2026 session architecture (docs/SESSION-ARCHITECTURE.md), every app
|
|
9
|
-
* keeps its OWN first-party session on its OWN domain. For non-`oxy.so` apps
|
|
10
|
-
* this is the app's own same-site backend (e.g. `https://api.mention.earth`),
|
|
11
|
-
* whose session bridge forwards the user's refresh credential to
|
|
12
|
-
* `api.oxy.so`. For `*.oxy.so` apps this is omitted and falls back to
|
|
13
|
-
* `baseURL` (`https://api.oxy.so`), so their behavior is unchanged.
|
|
14
|
-
*
|
|
15
|
-
* Resolve via {@link OxyServices.getSessionBaseUrl}; when unset it returns
|
|
16
|
-
* `baseURL`. This is purely additive — no refresh/auth logic reads it yet.
|
|
17
|
-
*/
|
|
18
|
-
sessionBaseUrl?: string;
|
|
19
5
|
authWebUrl?: string;
|
|
20
6
|
authRedirectUri?: string;
|
|
21
7
|
/**
|
|
@@ -110,7 +96,6 @@ export interface User {
|
|
|
110
96
|
phone?: string;
|
|
111
97
|
address?: string;
|
|
112
98
|
birthday?: string;
|
|
113
|
-
location?: string;
|
|
114
99
|
website?: string;
|
|
115
100
|
createdAt?: string;
|
|
116
101
|
updatedAt?: string;
|
|
@@ -584,55 +569,3 @@ export interface UpdateDeviceNameResponse {
|
|
|
584
569
|
message: string;
|
|
585
570
|
deviceName: string;
|
|
586
571
|
}
|
|
587
|
-
/**
|
|
588
|
-
* Minimal user shape included in a `RefreshAllAccount` entry. The server
|
|
589
|
-
* projects a small whitelist (`username name avatar email color`) so the
|
|
590
|
-
* client can render the account chooser without an extra `/users/me` round
|
|
591
|
-
* trip per account.
|
|
592
|
-
*
|
|
593
|
-
* `avatar` and `color` are `string | null` because they are stored as nullable
|
|
594
|
-
* fields in the user document.
|
|
595
|
-
*/
|
|
596
|
-
export interface RefreshAllAccountUser {
|
|
597
|
-
id: string;
|
|
598
|
-
username: string;
|
|
599
|
-
/**
|
|
600
|
-
* Structured human name as emitted by `formatUserResponse` (the canonical
|
|
601
|
-
* {@link UserNameResponse} `{ first?, last?, full? }` subdocument), NOT a bare
|
|
602
|
-
* string. The server projects `name` verbatim from the user document. The
|
|
603
|
-
* single source of truth is `@oxyhq/contracts`.
|
|
604
|
-
*/
|
|
605
|
-
name: UserNameResponse;
|
|
606
|
-
avatar?: string | null;
|
|
607
|
-
email?: string;
|
|
608
|
-
color?: string | null;
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* One rotated account entry returned by `POST /auth/refresh-all`. `authuser` is
|
|
612
|
-
* the device-local slot index (0..N-1) the cookie was bound to.
|
|
613
|
-
*/
|
|
614
|
-
export interface RefreshAllAccount {
|
|
615
|
-
authuser: number;
|
|
616
|
-
accessToken: string;
|
|
617
|
-
expiresAt: string;
|
|
618
|
-
sessionId: string;
|
|
619
|
-
user: RefreshAllAccountUser | null;
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Wire shape of `POST /auth/refresh-all`. Always 200 with a (possibly empty)
|
|
623
|
-
* accounts array — 401 means "no accounts signed in on this device" and is
|
|
624
|
-
* normalised to `{ accounts: [] }` at the SDK layer.
|
|
625
|
-
*/
|
|
626
|
-
export interface RefreshAllResponse {
|
|
627
|
-
accounts: RefreshAllAccount[];
|
|
628
|
-
}
|
|
629
|
-
/**
|
|
630
|
-
* Wire shape of `POST /auth/refresh` (single-slot refresh, optionally targeting
|
|
631
|
-
* a specific `?authuser=N` slot). The server always includes the numeric slot in
|
|
632
|
-
* the response.
|
|
633
|
-
*/
|
|
634
|
-
export interface RefreshCookieResponse {
|
|
635
|
-
accessToken: string;
|
|
636
|
-
expiresAt: string;
|
|
637
|
-
authuser: number;
|
|
638
|
-
}
|
|
@@ -7,11 +7,10 @@ export interface ClientSession {
|
|
|
7
7
|
userId?: string;
|
|
8
8
|
isCurrent?: boolean;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Absent on native (RN uses the bearer-protected session id directly).
|
|
10
|
+
* The account's ordinal slot (0..N) within the device's account set
|
|
11
|
+
* (`SessionAccount.authuser` in `@oxyhq/contracts`), projected from the
|
|
12
|
+
* device-first `DeviceSessionState` — used purely for stable Google-style
|
|
13
|
+
* account-chooser ordering, not for any token-refresh mechanism.
|
|
15
14
|
*/
|
|
16
15
|
authuser?: number;
|
|
17
16
|
}
|
|
@@ -23,5 +23,5 @@ export type { SafeFetchOptions, SafeFetchResult, SsrfCheckFail, SsrfCheckOk, Ssr
|
|
|
23
23
|
export { createOxyCors } from './cors';
|
|
24
24
|
export type { OxyCorsOptions } from './cors';
|
|
25
25
|
export { verifySecret } from './verifySecret';
|
|
26
|
-
export { registrableApex } from '../utils/
|
|
26
|
+
export { registrableApex } from '../utils/registrableApex';
|
|
27
27
|
export { SSO_CALLBACK_PATH } from '../utils/ssoBounce';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type DeviceSessionState } from '@oxyhq/contracts';
|
|
2
2
|
import type { MinimalSocket, SocketIOFactory } from './socketLoader';
|
|
3
3
|
export interface TokenTransport {
|
|
4
|
-
/** Ensure this app holds a per-domain access token for state.activeAccountId (mint via
|
|
4
|
+
/** Ensure this app holds a per-domain access token for state.activeAccountId (mint via the persisted refresh family / shared keychain). Best-effort. */
|
|
5
5
|
ensureActiveToken(state: DeviceSessionState): Promise<void>;
|
|
6
6
|
}
|
|
7
7
|
export interface SessionClientHost {
|
|
@@ -16,6 +16,16 @@ export interface SessionClientHost {
|
|
|
16
16
|
}
|
|
17
17
|
export interface SessionClientOptions {
|
|
18
18
|
transport?: TokenTransport;
|
|
19
|
+
/**
|
|
20
|
+
* Invoked when an APPLIED state has zero accounts — i.e. a device
|
|
21
|
+
* signout-all removed the last account from this device set. Providers use
|
|
22
|
+
* this to clear the persisted {@link AuthStateStore} so a reload does not
|
|
23
|
+
* try to restore a session that no longer exists on the device.
|
|
24
|
+
*
|
|
25
|
+
* Only fires when a state is actually applied (revision advanced), never on
|
|
26
|
+
* a stale/rejected push. Exceptions thrown by the callback are isolated.
|
|
27
|
+
*/
|
|
28
|
+
onUnauthenticated?: () => void;
|
|
19
29
|
/**
|
|
20
30
|
* Statically-injected `socket.io-client` factory (its `io` export).
|
|
21
31
|
* `@oxyhq/services` and `@oxyhq/auth` list `socket.io-client` as a real
|
|
@@ -60,6 +70,21 @@ export declare class SessionClient {
|
|
|
60
70
|
all: true;
|
|
61
71
|
}): Promise<void>;
|
|
62
72
|
addCurrentAccount(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Register the just-signed-in account into the device set AND make it the
|
|
75
|
+
* ACTIVE account — the explicit user-intent activation a sign-in UI performs.
|
|
76
|
+
*
|
|
77
|
+
* `addCurrentAccount` alone honors the server's `activate:'if-empty'` policy
|
|
78
|
+
* (a new account does NOT steal focus from an already-active one), which is
|
|
79
|
+
* correct for a background/silent add but wrong right after a deliberate
|
|
80
|
+
* sign-in. This adds, then switches to the target so the UI lands on the
|
|
81
|
+
* account the user just authenticated.
|
|
82
|
+
*
|
|
83
|
+
* @param accountId - The signed-in account id (e.g. `session.user.id`). When
|
|
84
|
+
* omitted, falls back to the host's current-account ref. If neither
|
|
85
|
+
* resolves, the add still applies and no switch is performed.
|
|
86
|
+
*/
|
|
87
|
+
registerAndActivate(accountId?: string): Promise<void>;
|
|
63
88
|
start(): Promise<void>;
|
|
64
89
|
stop(): void;
|
|
65
90
|
private connectSocket;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted auth state — ONE shape, web + native.
|
|
3
|
+
*
|
|
4
|
+
* The device-first session model (auth-centralization wave 1) persists the
|
|
5
|
+
* rotating refresh-token family head per ORIGIN so a reload restores the
|
|
6
|
+
* session locally without a redirect. This module is the storage seam: a tiny
|
|
7
|
+
* `load / save / clear` interface plus platform factories, so the cold boot
|
|
8
|
+
* (`coldBootV2`) and the unified refresh handler (`refresh.ts`) never touch a
|
|
9
|
+
* platform storage API directly.
|
|
10
|
+
*
|
|
11
|
+
* Platform-agnostic — the native factory takes an INJECTED key/value store
|
|
12
|
+
* (`@oxyhq/services` passes a SecureStore-backed adapter) so `@oxyhq/core`
|
|
13
|
+
* never imports `expo-secure-store`. The web factory is self-contained
|
|
14
|
+
* (`localStorage`) and degrades to in-memory when storage is unavailable
|
|
15
|
+
* (sandboxed iframe `SecurityError`, private-mode quota, SSR).
|
|
16
|
+
*
|
|
17
|
+
* ESM-safe (no `require()`).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The persisted session credential set for a single origin.
|
|
21
|
+
*
|
|
22
|
+
* `refreshToken` is the rotating single-use family head; `sessionId` + `userId`
|
|
23
|
+
* identify the owning device session and account. `deviceToken` is the opaque,
|
|
24
|
+
* add-only device attribution token (mirrored to the shared keychain on native
|
|
25
|
+
* so every Oxy app on one phone shares one DeviceSession).
|
|
26
|
+
*
|
|
27
|
+
* `accessToken` + `expiresAt` are OPTIONAL warm-boot fields. Persisting them
|
|
28
|
+
* lets the cold boot plant a still-valid access token on the very first paint
|
|
29
|
+
* WITHOUT a blocking `/auth/refresh-token` round-trip — the proactive scheduler
|
|
30
|
+
* then rotates it in the background. They are a strict optimization: the store
|
|
31
|
+
* is fully functional (via `refreshToken`) when they are absent or stale, and
|
|
32
|
+
* the access token is short-lived, so persisting it adds no exposure the
|
|
33
|
+
* already-persisted refresh token does not (see the plan's XSS risk note — the
|
|
34
|
+
* refresh token is the dominant secret either way).
|
|
35
|
+
*/
|
|
36
|
+
export interface PersistedAuthState {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
refreshToken: string;
|
|
39
|
+
userId: string;
|
|
40
|
+
deviceToken?: string;
|
|
41
|
+
/** Optional warm-boot access token (short-lived; see interface docs). */
|
|
42
|
+
accessToken?: string;
|
|
43
|
+
/** Optional warm-boot access-token expiry, ISO-8601. */
|
|
44
|
+
expiresAt?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The storage seam consumed by the cold boot and the refresh handler. Async
|
|
48
|
+
* throughout so one interface fits both synchronous web `localStorage` and
|
|
49
|
+
* asynchronous native SecureStore/AsyncStorage.
|
|
50
|
+
*
|
|
51
|
+
* Two lifetimes:
|
|
52
|
+
* - The SESSION credential blob (`load`/`save`/`clear`) is per-sign-in and is
|
|
53
|
+
* wiped on `clear()` (sign-out).
|
|
54
|
+
* - The DEVICE token (`loadDeviceToken`/`saveDeviceToken`/`clearDeviceToken`)
|
|
55
|
+
* is long-lived device attribution that SURVIVES `clear()`: a signed-out
|
|
56
|
+
* browser is still the same device, and a later in-app (cross-apex,
|
|
57
|
+
* cookie-less) login sends this token so the new session joins the SAME
|
|
58
|
+
* server-side DeviceSession. Only an explicit device signout-all
|
|
59
|
+
* (`clearDeviceToken`) removes it.
|
|
60
|
+
*/
|
|
61
|
+
export interface AuthStateStore {
|
|
62
|
+
load(): Promise<PersistedAuthState | null>;
|
|
63
|
+
save(state: PersistedAuthState): Promise<void>;
|
|
64
|
+
clear(): Promise<void>;
|
|
65
|
+
loadDeviceToken(): Promise<string | null>;
|
|
66
|
+
saveDeviceToken(token: string): Promise<void>;
|
|
67
|
+
clearDeviceToken(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The minimal async key/value surface a native store must provide. Matches
|
|
71
|
+
* both `expo-secure-store` (wrapped) and `@react-native-async-storage`.
|
|
72
|
+
*/
|
|
73
|
+
export interface NativeKeyValueStorage {
|
|
74
|
+
getItem(key: string): Promise<string | null>;
|
|
75
|
+
setItem(key: string, value: string): Promise<void>;
|
|
76
|
+
removeItem(key: string): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Versioned storage key. The `.v1` suffix lets a future shape change ship a
|
|
80
|
+
* `.v2` key without reading a stale/incompatible `.v1` blob. Distinct from the
|
|
81
|
+
* `oxy_shared_*` keychain keys in `KeyManager`, so it never collides.
|
|
82
|
+
*/
|
|
83
|
+
export declare const AUTH_STATE_STORAGE_KEY = "oxy.auth.v1";
|
|
84
|
+
/**
|
|
85
|
+
* Storage key for the long-lived device-attribution token. Separate from
|
|
86
|
+
* {@link AUTH_STATE_STORAGE_KEY} because it must OUTLIVE a session `clear()`
|
|
87
|
+
* (sign-out) — the device is unchanged across sign-ins.
|
|
88
|
+
*/
|
|
89
|
+
export declare const DEVICE_TOKEN_STORAGE_KEY = "oxy.device.v1";
|
|
90
|
+
/**
|
|
91
|
+
* A process-lifetime, in-memory {@link AuthStateStore}. Used directly for
|
|
92
|
+
* tests/SSR and as the degraded fallback of the web store when `localStorage`
|
|
93
|
+
* is unreachable. Not durable across reloads — that is acceptable for the
|
|
94
|
+
* fallback because the alternative (throwing) would break cold boot entirely.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createMemoryAuthStateStore(): AuthStateStore;
|
|
97
|
+
/**
|
|
98
|
+
* A `localStorage`-backed {@link AuthStateStore} under the versioned
|
|
99
|
+
* {@link AUTH_STATE_STORAGE_KEY}.
|
|
100
|
+
*
|
|
101
|
+
* Resilience:
|
|
102
|
+
* - If `localStorage` is unreachable (sandboxed-iframe `SecurityError`, SSR),
|
|
103
|
+
* the whole store degrades to an in-memory {@link createMemoryAuthStateStore}
|
|
104
|
+
* for this page's lifetime — never throws on construction.
|
|
105
|
+
* - Individual `getItem`/`setItem`/`removeItem` are each wrapped: a read that
|
|
106
|
+
* throws yields `null`; a write that throws (quota, private mode) is
|
|
107
|
+
* swallowed. The persisted-refresh lane treats a failed persist as "no
|
|
108
|
+
* durable state" (falls back to the bootstrap hop) rather than crashing.
|
|
109
|
+
*/
|
|
110
|
+
export declare function createWebAuthStateStore(): AuthStateStore;
|
|
111
|
+
/**
|
|
112
|
+
* A native {@link AuthStateStore} over an injected async key/value store.
|
|
113
|
+
*
|
|
114
|
+
* `@oxyhq/core` never imports `expo-secure-store`; `@oxyhq/services` constructs
|
|
115
|
+
* the SecureStore-backed adapter and passes it here. Every operation is wrapped
|
|
116
|
+
* so a storage exception degrades gracefully (read → `null`, write → swallowed)
|
|
117
|
+
* exactly like the web store.
|
|
118
|
+
*/
|
|
119
|
+
export declare function createNativeAuthStateStore(storage: NativeKeyValueStorage): AuthStateStore;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified token refresh — THE single refresh implementation for web + native.
|
|
3
|
+
*
|
|
4
|
+
* Before device-first, refresh was duplicated: `@oxyhq/auth`'s
|
|
5
|
+
* `session/tokenRefresh.ts` (per-apex `/auth/silent` iframe) and
|
|
6
|
+
* `@oxyhq/services`'s `inSessionTokenRefresh.ts` (native shared-key). This
|
|
7
|
+
* module replaces both with ONE persisted-refresh-token rotation shared by
|
|
8
|
+
* every consumer:
|
|
9
|
+
*
|
|
10
|
+
* - `refreshPersistedSession` — arm 1 rotates the stored refresh-token family
|
|
11
|
+
* (`POST /auth/refresh-token`), planting + persisting the rotated pair; arm 2
|
|
12
|
+
* (native only) re-mints via the shared-keychain identity when there is no
|
|
13
|
+
* live refresh token. It is used BOTH reactively (wrapped as the
|
|
14
|
+
* `AuthRefreshHandler` installed on `HttpService`) AND proactively (the
|
|
15
|
+
* cold-boot `stored-tokens` step calls it directly).
|
|
16
|
+
* - `createAuthRefreshHandler` / `installAuthRefreshHandler` wire arm 1+2 into
|
|
17
|
+
* `HttpService.setAuthRefreshHandler`, keeping that layer's single-flight
|
|
18
|
+
* dedup + cooldown (this module does NOT reimplement them).
|
|
19
|
+
* - `startTokenRefreshScheduler` — a proactive scheduler (lifted from the
|
|
20
|
+
* better of the two prior duplicates, `@oxyhq/auth`'s `tokenRefresh.ts`),
|
|
21
|
+
* decoupled from any React / auth-sdk type: refreshes ~60s before `exp`,
|
|
22
|
+
* re-arms on token change + web tab-focus, `.unref?.()`s its timer in Node.
|
|
23
|
+
*
|
|
24
|
+
* Framework-free; no module-level mutable state.
|
|
25
|
+
*/
|
|
26
|
+
import type { OxyServices } from '../OxyServices';
|
|
27
|
+
import type { AuthRefreshHandler } from '../HttpService';
|
|
28
|
+
import type { AuthStateStore } from './authStateStore';
|
|
29
|
+
/**
|
|
30
|
+
* Lead time (ms) before access-token expiry at which the proactive scheduler
|
|
31
|
+
* refreshes. Mirrors `HttpService`'s per-request `TOKEN_REFRESH_LEAD_SECONDS`
|
|
32
|
+
* (60s) so the scheduled refresh and the request-time preflight refresh use
|
|
33
|
+
* the same window — the scheduler just fires it during idle/background.
|
|
34
|
+
*/
|
|
35
|
+
export declare const TOKEN_REFRESH_LEAD_MS = 60000;
|
|
36
|
+
export interface RefreshDeps {
|
|
37
|
+
oxy: OxyServices;
|
|
38
|
+
store: AuthStateStore;
|
|
39
|
+
/**
|
|
40
|
+
* Whether to fall back to the native shared-keychain re-mint (arm 2) when
|
|
41
|
+
* there is no live refresh token / arm 1 is revoked. Defaults to `isNative()`
|
|
42
|
+
* — web has no shared keychain. Exposed for tests.
|
|
43
|
+
*/
|
|
44
|
+
allowSharedKeyFallback?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Rotate the persisted session and return the fresh access token, or `null`
|
|
48
|
+
* when no arm could produce one.
|
|
49
|
+
*
|
|
50
|
+
* Arm 1 (`POST /auth/refresh-token`): if the store holds a refresh token, rotate
|
|
51
|
+
* it — on success plant + persist the rotated pair; on a REVOKED error clear the
|
|
52
|
+
* store; on a transient error leave the store and return `null`.
|
|
53
|
+
*
|
|
54
|
+
* Arm 2 (native shared-keychain): when there is no refresh token or arm 1 was
|
|
55
|
+
* revoked, re-mint via `signInWithSharedIdentity` (which plants tokens). The
|
|
56
|
+
* shared keychain — not the per-origin store — is the durable native credential,
|
|
57
|
+
* so this arm does not write the store.
|
|
58
|
+
*/
|
|
59
|
+
export declare function refreshPersistedSession(deps: RefreshDeps): Promise<string | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Build the reactive `AuthRefreshHandler` (arm 1 + arm 2). Install it via
|
|
62
|
+
* {@link installAuthRefreshHandler} or directly on
|
|
63
|
+
* `oxy.httpService.setAuthRefreshHandler`. `HttpService` owns single-flight
|
|
64
|
+
* dedup + cooldown, so the timer, the request-time preflight, and a 401 all
|
|
65
|
+
* collapse to one network attempt.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createAuthRefreshHandler(deps: RefreshDeps): AuthRefreshHandler;
|
|
68
|
+
/**
|
|
69
|
+
* Install the unified refresh handler on the owner client's `HttpService`.
|
|
70
|
+
* Returns a disposer that removes it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function installAuthRefreshHandler(deps: RefreshDeps): () => void;
|
|
73
|
+
/** Handle returned by {@link startTokenRefreshScheduler}; `dispose()` tears it down. */
|
|
74
|
+
export interface TokenRefreshSchedulerHandle {
|
|
75
|
+
dispose(): void;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Start the proactive refresh scheduler against `oxy`.
|
|
79
|
+
*
|
|
80
|
+
* Schedules a single timer to fire {@link TOKEN_REFRESH_LEAD_MS} before the
|
|
81
|
+
* current access token's `exp`, calling
|
|
82
|
+
* `oxy.httpService.refreshAccessToken('preflight')` (which runs the installed
|
|
83
|
+
* handler; deduped + cooldown-guarded). After every attempt it reschedules
|
|
84
|
+
* from the possibly-rotated token. It also reschedules whenever the token
|
|
85
|
+
* changes (a sign-out that clears the token cancels the timer) and, on web
|
|
86
|
+
* tab-focus, refreshes immediately if already inside the lead window (a
|
|
87
|
+
* long-hidden tab throttles timers, so the token can be expired on return).
|
|
88
|
+
*
|
|
89
|
+
* No-ops cleanly when there is no token or an opaque/no-`exp` token — the
|
|
90
|
+
* reactive 401 path stays the only refresh trigger in that case. The timer is
|
|
91
|
+
* `.unref?.()`-ed so it never keeps a Node/Jest event loop alive.
|
|
92
|
+
*/
|
|
93
|
+
export declare function startTokenRefreshScheduler(oxy: OxyServices): TokenRefreshSchedulerHandle;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export declare const isDev: () => boolean;
|
|
13
13
|
/**
|
|
14
14
|
* Log a debug message (only in development)
|
|
15
|
-
* @param prefix - Log prefix (e.g., '[
|
|
15
|
+
* @param prefix - Log prefix (e.g., '[ColdBoot]')
|
|
16
16
|
* @param args - Arguments to log
|
|
17
17
|
*/
|
|
18
18
|
export declare const debugLog: (prefix: string, ...args: unknown[]) => void;
|
|
@@ -30,12 +30,12 @@ export declare const debugWarn: (prefix: string, ...args: unknown[]) => void;
|
|
|
30
30
|
export declare const debugError: (prefix: string, ...args: unknown[]) => void;
|
|
31
31
|
/**
|
|
32
32
|
* Create a namespaced debug logger
|
|
33
|
-
* @param namespace - Logger namespace (e.g., '
|
|
33
|
+
* @param namespace - Logger namespace (e.g., 'ColdBoot', 'DeviceAuth')
|
|
34
34
|
* @returns Object with log, warn, error methods
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```ts
|
|
38
|
-
* const debug = createDebugLogger('
|
|
38
|
+
* const debug = createDebugLogger('ColdBoot');
|
|
39
39
|
* debug.log('Starting authentication');
|
|
40
40
|
* debug.warn('Token expires soon');
|
|
41
41
|
* debug.error('Authentication failed', error);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Shared account types and pure helper functions.
|
|
3
3
|
* Used by both @oxyhq/services (React Native) and @oxyhq/auth (Web) account stores.
|
|
4
4
|
*/
|
|
5
|
-
import type { RefreshAllAccount } from '../models/interfaces';
|
|
6
5
|
export interface QuickAccount {
|
|
7
6
|
sessionId: string;
|
|
8
7
|
userId?: string;
|
|
@@ -12,9 +11,8 @@ export interface QuickAccount {
|
|
|
12
11
|
avatarUrl?: string;
|
|
13
12
|
/**
|
|
14
13
|
* Device-local account slot index, 0..N-1 (Google-style multi-account).
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* remain valid; web flows always populate it after `refreshAllSessions`.
|
|
14
|
+
* Optional so that pre-multi-account QuickAccounts (sessionId-only) remain
|
|
15
|
+
* valid; the device session set populates it where available.
|
|
18
16
|
*/
|
|
19
17
|
authuser?: number;
|
|
20
18
|
/**
|
|
@@ -94,19 +92,6 @@ export declare const createQuickAccount: (sessionId: string, userData: {
|
|
|
94
92
|
} | string;
|
|
95
93
|
avatar?: string | null;
|
|
96
94
|
}, existingAccount?: QuickAccount, getFileDownloadUrl?: (fileId: string, variant: string) => string) => QuickAccount;
|
|
97
|
-
/**
|
|
98
|
-
* Merge a fresh `/auth/refresh-all` snapshot into an existing QuickAccount
|
|
99
|
-
* list, preserving any cached fields (`avatarUrl`) for slots that didn't
|
|
100
|
-
* change. The fresh response is canonical: the resulting list contains EXACTLY
|
|
101
|
-
* the slots present in `fresh`, sorted by `authuser` ascending. Stale stored
|
|
102
|
-
* accounts that no longer appear in `fresh` are dropped (the server already
|
|
103
|
-
* authoritatively cleared the corresponding cookie).
|
|
104
|
-
*
|
|
105
|
-
* @param stored Previously persisted QuickAccount list (any order).
|
|
106
|
-
* @param fresh Server's authoritative refresh-all response.
|
|
107
|
-
* @returns Canonical merged list, sorted by `authuser` asc.
|
|
108
|
-
*/
|
|
109
|
-
export declare const mergeAccountsFromRefreshAll: (stored: QuickAccount[] | undefined, fresh: RefreshAllAccount[]) => QuickAccount[];
|
|
110
95
|
/**
|
|
111
96
|
* Return the account's preferred Bloom color preset, or `null` if it has no
|
|
112
97
|
* preference. Centralises the `color ?? null` normalisation so consumers can
|
|
@@ -1,42 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Central IdP
|
|
2
|
+
* Central IdP apex constant.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* This module is intentionally pure: it performs no DOM access, reads no
|
|
12
|
-
* `window`/`location`, and has no side effects. It is the single source of
|
|
13
|
-
* truth for the central IdP origin so call sites never hardcode the literal.
|
|
14
|
-
*
|
|
15
|
-
* Note: this is distinct from `autoDetectAuthWebUrl` (per-apex `auth.<rp-apex>`
|
|
16
|
-
* derivation). The central-SSO path deliberately does NOT auto-detect per-apex
|
|
17
|
-
* IdPs — it is central only. An explicitly-configured `authWebUrl` still wins.
|
|
4
|
+
* The client SSO/FedCM resolvers (`resolveCentralAuthUrl`, `CENTRAL_AUTH_URL`)
|
|
5
|
+
* were removed in the device-first / legacy-final cutovers. The lone survivor is
|
|
6
|
+
* `CENTRAL_IDP_APEX`, kept because it has a LIVE consumer —
|
|
7
|
+
* `@oxyhq/core/server`'s CORS helper (`server/cors.ts`'s `createOxyCors`)
|
|
8
|
+
* auto-allows `*.oxy.so` from it. That CORS use is permanent, so this stays
|
|
9
|
+
* past the SSO/FedCM teardown.
|
|
18
10
|
*/
|
|
19
11
|
/**
|
|
20
12
|
* The registrable apex (eTLD+1) of the Oxy ecosystem's central Identity
|
|
21
|
-
* Provider
|
|
22
|
-
*
|
|
23
|
-
* regardless of which per-apex `auth.<rp>` host served a given request.
|
|
24
|
-
*
|
|
25
|
-
* Kept as a standalone constant so the IdP worker and the SDK derive the same
|
|
26
|
-
* literal from one source of truth (the worker imports it to brand assertions).
|
|
13
|
+
* Provider, reachable at `auth.${CENTRAL_IDP_APEX}`. Single source of truth so
|
|
14
|
+
* the CORS helper (and anything else that needs the central apex) never drifts.
|
|
27
15
|
*/
|
|
28
16
|
export declare const CENTRAL_IDP_APEX = "oxy.so";
|
|
29
|
-
/**
|
|
30
|
-
* The canonical central Identity Provider origin for the Oxy ecosystem.
|
|
31
|
-
* No trailing slash. Derived from {@link CENTRAL_IDP_APEX} so the apex and the
|
|
32
|
-
* full origin never drift apart.
|
|
33
|
-
*/
|
|
34
|
-
export declare const CENTRAL_AUTH_URL = "https://auth.oxy.so";
|
|
35
|
-
/**
|
|
36
|
-
* Resolve the central IdP origin, honouring an explicit override.
|
|
37
|
-
*
|
|
38
|
-
* @param explicit - A caller-supplied auth web URL, or `undefined`/empty to use
|
|
39
|
-
* the central default. An explicit non-empty value always wins.
|
|
40
|
-
* @returns The explicit value when provided, otherwise {@link CENTRAL_AUTH_URL}.
|
|
41
|
-
*/
|
|
42
|
-
export declare function resolveCentralAuthUrl(explicit?: string): string;
|
|
@@ -3,19 +3,21 @@
|
|
|
3
3
|
* authentication resolution.
|
|
4
4
|
*
|
|
5
5
|
* On a fresh page load / app launch the SDK may have several ways to recover an
|
|
6
|
-
* existing session (
|
|
7
|
-
*
|
|
8
|
-
* order
|
|
9
|
-
* skipped. This module encodes exactly that contract and
|
|
6
|
+
* existing session (a persisted refresh-token family, a shared-keychain
|
|
7
|
+
* identity, a cross-domain boot-fragment return, ...). They must be attempted
|
|
8
|
+
* in a deterministic order, and the FIRST one that yields a session wins —
|
|
9
|
+
* every later step is skipped. This module encodes exactly that contract and
|
|
10
|
+
* nothing else.
|
|
10
11
|
*
|
|
11
12
|
* Design constraints (all enforced):
|
|
12
13
|
* - PURE: no DOM, no `navigator`, no `window`, no React, no platform globals.
|
|
13
14
|
* - NO module-level mutable state. Every call to {@link runColdBoot} is fully
|
|
14
15
|
* self-contained, so it is safe under bundler re-evaluation (e.g. the Metro
|
|
15
|
-
* web bundle
|
|
16
|
-
*
|
|
17
|
-
* - Architecture-agnostic:
|
|
18
|
-
*
|
|
16
|
+
* web bundle — the reason any run-once guard for a step must live in the
|
|
17
|
+
* calling consumer, never in a core module-level singleton).
|
|
18
|
+
* - Architecture-agnostic: it knows nothing about HOW a step resolves a
|
|
19
|
+
* session; `runSessionColdBoot` (`boot/coldBootV2.ts`) is the current
|
|
20
|
+
* device-first consumer.
|
|
19
21
|
*
|
|
20
22
|
* A step is skipped (without running) when its `enabled` predicate returns
|
|
21
23
|
* false. Any thrown error — from either `enabled` or `run` — is reported via
|
|
@@ -92,15 +94,16 @@ export interface RunColdBootOptions<S> {
|
|
|
92
94
|
* fails to settle before the deadline, the runner abandons the await for that
|
|
93
95
|
* step (reporting it via `onStepDeadline`) and CONTINUES to the next step,
|
|
94
96
|
* each now racing against an already-expired deadline. This is deliberate:
|
|
95
|
-
* the runner keeps iterating so the TERMINAL step (e.g.
|
|
96
|
-
* whose `run()` performs its side effect
|
|
97
|
-
* `await`) still gets to fire. A step that
|
|
98
|
-
* the deadline simply doesn't settle and is
|
|
97
|
+
* the runner keeps iterating so the TERMINAL step (e.g. `coldBootV2`'s
|
|
98
|
+
* `bootstrap-hop`, whose `run()` performs its navigation side effect
|
|
99
|
+
* synchronously before its first `await`) still gets to fire. A step that
|
|
100
|
+
* has nothing to contribute after the deadline simply doesn't settle and is
|
|
101
|
+
* skipped in turn.
|
|
99
102
|
*
|
|
100
103
|
* Per-step timeouts inside `run()` remain the first line of defense and
|
|
101
104
|
* should keep every step well under this budget on a healthy load; this only
|
|
102
|
-
* trips when one of them regresses (
|
|
103
|
-
* omitted there is no overall deadline.
|
|
105
|
+
* trips when one of them regresses (a step hanging past its own timeout).
|
|
106
|
+
* When omitted there is no overall deadline.
|
|
104
107
|
*/
|
|
105
108
|
readonly overallDeadlineMs?: number;
|
|
106
109
|
/**
|
|
@@ -33,3 +33,17 @@ export declare function isAndroid(): boolean;
|
|
|
33
33
|
* This allows lazy detection in environments where react-native is available
|
|
34
34
|
*/
|
|
35
35
|
export declare function setPlatformOS(os: PlatformOS): void;
|
|
36
|
+
/**
|
|
37
|
+
* True only in a real web browser (a DOM is present), false on React Native
|
|
38
|
+
* and Node/SSR.
|
|
39
|
+
*
|
|
40
|
+
* Native defines a global `window` but no `document`, so the DOM probe — not a
|
|
41
|
+
* bare `window` check — is the reliable discriminator. This is the single
|
|
42
|
+
* source of truth consumed by `@oxyhq/services` and `@oxyhq/auth` (both dropped
|
|
43
|
+
* their local copies), so every consumer shares the exact same predicate.
|
|
44
|
+
*
|
|
45
|
+
* NOTE: this is a live runtime probe (not the cached `getPlatformOS()` verdict)
|
|
46
|
+
* because it must reflect the actual DOM availability at call time in the
|
|
47
|
+
* consumer's bundle.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isWebBrowser(): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registrable-apex (eTLD+1) host kernel.
|
|
3
|
+
*
|
|
4
|
+
* The client FAPI auto-detection helper was removed in the device-first cutover
|
|
5
|
+
* (which is why this file is now named for what it actually is, not the old
|
|
6
|
+
* `fapiAutoDetect`). What survives is the pure registrable-domain kernel, still
|
|
7
|
+
* used server-side by the api's device-first same-apex trust checks
|
|
8
|
+
* (`deviceAuth.ts`'s `POST /auth/device/web-session`, via `sameSite.ts`'s
|
|
9
|
+
* `isSameSiteTrustedRequest`) and the `@oxyhq/core/server` CORS/re-export layer.
|
|
10
|
+
*
|
|
11
|
+
* `registrableApex` is NOT legacy — the device-first same-apex check is a live
|
|
12
|
+
* consumer, so this kernel stays regardless of the SSO/FedCM removal.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Compute the bare registrable apex (eTLD+1) of a hostname using the Public
|
|
16
|
+
* Suffix List, including private hosted suffixes.
|
|
17
|
+
*
|
|
18
|
+
* Performs NO protocol handling and builds NO URL — it only answers "what is
|
|
19
|
+
* the registrable domain of this host, or is that undefinable?".
|
|
20
|
+
*
|
|
21
|
+
* Returns `null` (apex undefinable) for:
|
|
22
|
+
* - empty input;
|
|
23
|
+
* - IPv4 literals (`192.168.1.10`);
|
|
24
|
+
* - IPv6 literals or any host carrying a port (`[::1]`, anything with `:`);
|
|
25
|
+
* - single-label hosts (`intranet`, `localhost`);
|
|
26
|
+
* - public suffixes without a registrable label (e.g. `co.uk`, `github.io`).
|
|
27
|
+
*
|
|
28
|
+
* @param hostname - A bare hostname (no scheme), e.g. `www.mention.earth`.
|
|
29
|
+
* @returns The eTLD+1 (`mention.earth`), or `null` when undefinable.
|
|
30
|
+
*/
|
|
31
|
+
export declare function registrableApex(hostname: string): string | null;
|