@oxyhq/core 5.5.0 → 6.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.
Files changed (181) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +6 -3
  3. package/dist/cjs/OxyServices.base.js +7 -102
  4. package/dist/cjs/boot/coldBootV2.js +350 -0
  5. package/dist/cjs/boot/deviceBootReturn.js +152 -0
  6. package/dist/cjs/crypto/keyManager.js +95 -0
  7. package/dist/cjs/i18n/locales/en-US.json +13 -1
  8. package/dist/cjs/i18n/locales/es-ES.json +13 -1
  9. package/dist/cjs/i18n/locales/locales/en-US.json +13 -1
  10. package/dist/cjs/i18n/locales/locales/es-ES.json +13 -1
  11. package/dist/cjs/index.js +47 -44
  12. package/dist/cjs/mixins/OxyServices.accounts.js +6 -13
  13. package/dist/cjs/mixins/OxyServices.auth.js +66 -201
  14. package/dist/cjs/mixins/OxyServices.authorizedApps.js +38 -0
  15. package/dist/cjs/mixins/OxyServices.deviceBoot.js +119 -0
  16. package/dist/cjs/mixins/index.js +13 -17
  17. package/dist/cjs/session/SessionClient.js +40 -1
  18. package/dist/cjs/session/authStateStore.js +284 -0
  19. package/dist/cjs/session/refresh.js +264 -0
  20. package/dist/cjs/utils/accountUtils.js +1 -55
  21. package/dist/cjs/utils/authWebUrl.js +6 -15
  22. package/dist/cjs/utils/fapiAutoDetect.js +16 -64
  23. package/dist/cjs/utils/platform.js +19 -0
  24. package/dist/cjs/utils/ssoBounce.js +15 -362
  25. package/dist/cjs/utils/validationUtils.js +57 -0
  26. package/dist/esm/.tsbuildinfo +1 -1
  27. package/dist/esm/HttpService.js +6 -3
  28. package/dist/esm/OxyServices.base.js +7 -102
  29. package/dist/esm/boot/coldBootV2.js +344 -0
  30. package/dist/esm/boot/deviceBootReturn.js +146 -0
  31. package/dist/esm/crypto/keyManager.js +95 -0
  32. package/dist/esm/i18n/locales/en-US.json +13 -1
  33. package/dist/esm/i18n/locales/es-ES.json +13 -1
  34. package/dist/esm/i18n/locales/locales/en-US.json +13 -1
  35. package/dist/esm/i18n/locales/locales/es-ES.json +13 -1
  36. package/dist/esm/index.js +28 -18
  37. package/dist/esm/mixins/OxyServices.accounts.js +6 -13
  38. package/dist/esm/mixins/OxyServices.auth.js +66 -201
  39. package/dist/esm/mixins/OxyServices.authorizedApps.js +35 -0
  40. package/dist/esm/mixins/OxyServices.deviceBoot.js +116 -0
  41. package/dist/esm/mixins/index.js +13 -17
  42. package/dist/esm/session/SessionClient.js +40 -1
  43. package/dist/esm/session/authStateStore.js +278 -0
  44. package/dist/esm/session/refresh.js +257 -0
  45. package/dist/esm/utils/accountUtils.js +0 -53
  46. package/dist/esm/utils/authWebUrl.js +6 -14
  47. package/dist/esm/utils/fapiAutoDetect.js +16 -63
  48. package/dist/esm/utils/platform.js +18 -0
  49. package/dist/esm/utils/ssoBounce.js +14 -345
  50. package/dist/esm/utils/validationUtils.js +56 -0
  51. package/dist/types/.tsbuildinfo +1 -1
  52. package/dist/types/HttpService.d.ts +13 -0
  53. package/dist/types/OxyServices.base.d.ts +0 -52
  54. package/dist/types/OxyServices.d.ts +0 -25
  55. package/dist/types/boot/coldBootV2.d.ts +76 -0
  56. package/dist/types/boot/deviceBootReturn.d.ts +83 -0
  57. package/dist/types/crypto/keyManager.d.ts +21 -0
  58. package/dist/types/index.d.ts +15 -21
  59. package/dist/types/mixins/OxyServices.accounts.d.ts +0 -2
  60. package/dist/types/mixins/OxyServices.analytics.d.ts +0 -2
  61. package/dist/types/mixins/OxyServices.appData.d.ts +0 -2
  62. package/dist/types/mixins/OxyServices.assets.d.ts +0 -2
  63. package/dist/types/mixins/OxyServices.auth.d.ts +35 -77
  64. package/dist/types/mixins/{OxyServices.redirect.d.ts → OxyServices.authorizedApps.d.ts} +35 -33
  65. package/dist/types/mixins/OxyServices.civic.d.ts +0 -2
  66. package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -2
  67. package/dist/types/mixins/OxyServices.contacts.d.ts +0 -2
  68. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +110 -0
  69. package/dist/types/mixins/OxyServices.devices.d.ts +0 -2
  70. package/dist/types/mixins/OxyServices.features.d.ts +0 -2
  71. package/dist/types/mixins/OxyServices.identity.d.ts +0 -2
  72. package/dist/types/mixins/OxyServices.language.d.ts +0 -2
  73. package/dist/types/mixins/OxyServices.links.d.ts +0 -2
  74. package/dist/types/mixins/OxyServices.location.d.ts +0 -2
  75. package/dist/types/mixins/OxyServices.nodes.d.ts +0 -2
  76. package/dist/types/mixins/OxyServices.payment.d.ts +0 -2
  77. package/dist/types/mixins/OxyServices.privacy.d.ts +0 -2
  78. package/dist/types/mixins/OxyServices.reputation.d.ts +0 -2
  79. package/dist/types/mixins/OxyServices.security.d.ts +0 -2
  80. package/dist/types/mixins/OxyServices.topics.d.ts +0 -2
  81. package/dist/types/mixins/OxyServices.user.d.ts +0 -2
  82. package/dist/types/mixins/OxyServices.utility.d.ts +0 -2
  83. package/dist/types/mixins/index.d.ts +6 -9
  84. package/dist/types/models/interfaces.d.ts +0 -67
  85. package/dist/types/session/SessionClient.d.ts +25 -0
  86. package/dist/types/session/authStateStore.d.ts +119 -0
  87. package/dist/types/session/refresh.d.ts +93 -0
  88. package/dist/types/utils/accountUtils.d.ts +0 -14
  89. package/dist/types/utils/authWebUrl.d.ts +6 -12
  90. package/dist/types/utils/fapiAutoDetect.d.ts +15 -38
  91. package/dist/types/utils/platform.d.ts +14 -0
  92. package/dist/types/utils/ssoBounce.d.ts +14 -280
  93. package/dist/types/utils/validationUtils.d.ts +15 -0
  94. package/package.json +2 -2
  95. package/src/HttpService.ts +19 -3
  96. package/src/OxyServices.base.ts +7 -112
  97. package/src/OxyServices.ts +0 -38
  98. package/src/boot/__tests__/coldBootV2.test.ts +317 -0
  99. package/src/boot/__tests__/deviceBootReturn.test.ts +158 -0
  100. package/src/boot/coldBootV2.ts +426 -0
  101. package/src/boot/deviceBootReturn.ts +195 -0
  102. package/src/crypto/__tests__/sharedDeviceToken.test.ts +24 -0
  103. package/src/crypto/keyManager.ts +101 -0
  104. package/src/i18n/locales/en-US.json +13 -1
  105. package/src/i18n/locales/es-ES.json +13 -1
  106. package/src/index.ts +74 -65
  107. package/src/mixins/OxyServices.accounts.ts +6 -13
  108. package/src/mixins/OxyServices.auth.ts +78 -253
  109. package/src/mixins/OxyServices.authorizedApps.ts +75 -0
  110. package/src/mixins/OxyServices.deviceBoot.ts +146 -0
  111. package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +107 -0
  112. package/src/mixins/__tests__/accounts.test.ts +17 -44
  113. package/src/mixins/__tests__/authorizedApps.test.ts +63 -0
  114. package/src/mixins/__tests__/passwordSignIn.test.ts +91 -0
  115. package/src/mixins/index.ts +16 -22
  116. package/src/models/interfaces.ts +0 -79
  117. package/src/session/SessionClient.ts +52 -1
  118. package/src/session/__tests__/SessionClient.additive.test.ts +92 -0
  119. package/src/session/__tests__/SessionClient.rest.test.ts +25 -0
  120. package/src/session/__tests__/SessionClient.state.test.ts +18 -5
  121. package/src/session/__tests__/authStateStore.test.ts +209 -0
  122. package/src/session/__tests__/refresh.test.ts +256 -0
  123. package/src/session/authStateStore.ts +335 -0
  124. package/src/session/refresh.ts +334 -0
  125. package/src/utils/__tests__/authWebUrl.test.ts +5 -29
  126. package/src/utils/__tests__/fapiAutoDetect.test.ts +5 -126
  127. package/src/utils/__tests__/validationUtils.test.ts +30 -0
  128. package/src/utils/accountUtils.ts +0 -62
  129. package/src/utils/authWebUrl.ts +6 -15
  130. package/src/utils/fapiAutoDetect.ts +16 -60
  131. package/src/utils/platform.ts +21 -0
  132. package/src/utils/ssoBounce.ts +14 -393
  133. package/src/utils/validationUtils.ts +62 -0
  134. package/dist/cjs/AuthManager.js +0 -1110
  135. package/dist/cjs/AuthManagerTypes.js +0 -13
  136. package/dist/cjs/CrossDomainAuth.js +0 -206
  137. package/dist/cjs/mixins/OxyServices.fedcm.js +0 -823
  138. package/dist/cjs/mixins/OxyServices.redirect.js +0 -95
  139. package/dist/cjs/mixins/OxyServices.silent.js +0 -204
  140. package/dist/cjs/mixins/OxyServices.sso.js +0 -208
  141. package/dist/cjs/utils/ssoEstablish.js +0 -110
  142. package/dist/cjs/utils/ssoReturn.js +0 -275
  143. package/dist/esm/AuthManager.js +0 -1105
  144. package/dist/esm/AuthManagerTypes.js +0 -12
  145. package/dist/esm/CrossDomainAuth.js +0 -201
  146. package/dist/esm/mixins/OxyServices.fedcm.js +0 -821
  147. package/dist/esm/mixins/OxyServices.redirect.js +0 -92
  148. package/dist/esm/mixins/OxyServices.silent.js +0 -202
  149. package/dist/esm/mixins/OxyServices.sso.js +0 -204
  150. package/dist/esm/utils/ssoEstablish.js +0 -107
  151. package/dist/esm/utils/ssoReturn.js +0 -271
  152. package/dist/types/AuthManager.d.ts +0 -380
  153. package/dist/types/AuthManagerTypes.d.ts +0 -81
  154. package/dist/types/CrossDomainAuth.d.ts +0 -164
  155. package/dist/types/mixins/OxyServices.fedcm.d.ts +0 -331
  156. package/dist/types/mixins/OxyServices.silent.d.ts +0 -132
  157. package/dist/types/mixins/OxyServices.sso.d.ts +0 -138
  158. package/dist/types/utils/ssoEstablish.d.ts +0 -85
  159. package/dist/types/utils/ssoReturn.d.ts +0 -156
  160. package/src/AuthManager.ts +0 -1269
  161. package/src/AuthManagerTypes.ts +0 -86
  162. package/src/CrossDomainAuth.ts +0 -243
  163. package/src/__tests__/authManager.cookiePath.test.ts +0 -390
  164. package/src/__tests__/authManager.security.test.ts +0 -377
  165. package/src/__tests__/crossDomainAuth.test.ts +0 -116
  166. package/src/__tests__/establishDeviceRefreshSlot.test.ts +0 -221
  167. package/src/mixins/OxyServices.fedcm.ts +0 -1026
  168. package/src/mixins/OxyServices.redirect.ts +0 -122
  169. package/src/mixins/OxyServices.silent.ts +0 -272
  170. package/src/mixins/OxyServices.sso.ts +0 -261
  171. package/src/mixins/__tests__/constructorAuthWebUrl.test.ts +0 -85
  172. package/src/mixins/__tests__/fedcm.test.ts +0 -667
  173. package/src/mixins/__tests__/sessionBaseUrl.test.ts +0 -61
  174. package/src/mixins/__tests__/silent.test.ts +0 -102
  175. package/src/mixins/__tests__/sso.test.ts +0 -228
  176. package/src/utils/__tests__/consumeSsoReturn.test.ts +0 -816
  177. package/src/utils/__tests__/ssoBounce.test.ts +0 -219
  178. package/src/utils/__tests__/ssoEstablish.test.ts +0 -204
  179. package/src/utils/__tests__/ssoReturn.test.ts +0 -276
  180. package/src/utils/ssoEstablish.ts +0 -174
  181. package/src/utils/ssoReturn.ts +0 -389
@@ -1,132 +0,0 @@
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
- createLinkedClient(config: import("../OxyServices.base").OxyConfig): import("..").LinkedHttpClient;
86
- getMetrics(): {
87
- totalRequests: number;
88
- successfulRequests: number;
89
- failedRequests: number;
90
- cacheHits: number;
91
- cacheMisses: number;
92
- averageResponseTime: number;
93
- };
94
- clearCache(): void;
95
- clearCacheEntry(key: string): void;
96
- clearCacheByPrefix(prefix: string): number;
97
- getCacheStats(): {
98
- size: number;
99
- hits: number;
100
- misses: number;
101
- hitRate: number;
102
- };
103
- getCloudURL(): string;
104
- setTokens(accessToken: string): void;
105
- clearTokens(): void;
106
- onTokensChanged(listener: (accessToken: string | null) => void): () => void;
107
- _cachedUserId: string | null | undefined;
108
- _cachedAccessToken: string | null;
109
- getCurrentUserId(): string | null;
110
- hasValidToken(): boolean;
111
- getAccessToken(): string | null;
112
- establishDeviceRefreshSlot(): Promise<number | null>;
113
- getAccessTokenExpiry(): number | null;
114
- waitForAuth(timeoutMs?: number): Promise<boolean>;
115
- withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
116
- maxRetries?: number;
117
- retryDelay?: number;
118
- authTimeoutMs?: number;
119
- }): Promise<T_1>;
120
- validate(): Promise<boolean>;
121
- handleError(error: unknown): Error;
122
- healthCheck(): Promise<{
123
- status: string;
124
- users?: number;
125
- timestamp?: string;
126
- [key: string]: any;
127
- }>;
128
- };
129
- readonly DEFAULT_AUTH_URL: "https://auth.oxy.so";
130
- readonly SILENT_TIMEOUT: 5000;
131
- } & T;
132
- export { OxyServicesSilentAuthMixin as SilentAuthMixin };
@@ -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,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>;