@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,81 +0,0 @@
1
- /**
2
- * AuthManager — public types for the multi-account cookie path.
3
- *
4
- * Lives in its own module (rather than the 670-line `models/interfaces.ts`)
5
- * so consumers can `import type` exactly the multi-account surface without
6
- * pulling in the full interfaces graph, and so `AuthManager.ts` stays
7
- * decoupled from the wire shapes — these types re-state the wire as the
8
- * AuthManager's in-memory representation.
9
- *
10
- * @module core/AuthManagerTypes
11
- */
12
- import type { RefreshAllAccountUser } from './models/interfaces';
13
- /**
14
- * One device-local account known to `AuthManager` in the cookie path.
15
- *
16
- * Built from a `POST /auth/refresh-all` entry, OR from a single
17
- * `POST /auth/refresh?authuser=N` rotation after a switch, OR from a
18
- * `handleAuthSuccess` call after a fresh login. The `accessToken` is held in
19
- * memory only — the refresh token never enters JS (it lives in the httpOnly
20
- * `oxy_rt_${authuser}` cookie).
21
- */
22
- export interface AuthManagerAccount {
23
- /** Device-local cookie slot index (0..N-1). */
24
- authuser: number;
25
- /** Server-side session id this slot is bound to. */
26
- sessionId: string;
27
- /**
28
- * Projected user shape from the wire (username/avatar/color/email).
29
- *
30
- * `null` when a refresh-via-cookie planted a fresh access token for a slot
31
- * that the AuthManager has no prior in-memory user metadata for. Callers (or
32
- * the AuthManager itself) are expected to hydrate the user shape via
33
- * `getCurrentUser()` after the token is planted; the chooser UI must render
34
- * the public-key fallback handle until the hydration completes.
35
- */
36
- user: RefreshAllAccountUser | null;
37
- /** Currently-valid access token for this slot (in-memory only). */
38
- accessToken: string;
39
- /** ISO-8601 expiry of the access token. */
40
- expiresAt: string;
41
- }
42
- /**
43
- * Outcome of `AuthManager.restoreFromCookies()`.
44
- *
45
- * `accounts` is sorted by `authuser` ascending (matching the server's
46
- * canonical ordering). `activeAuthuser` is whichever slot the AuthManager
47
- * picked as active — usually the persisted `oxy_active_authuser` if it
48
- * matched a returned slot, otherwise the lowest returned `authuser`, or
49
- * `null` if no accounts were restored.
50
- */
51
- export interface RestoreFromCookiesResult {
52
- accounts: AuthManagerAccount[];
53
- activeAuthuser: number | null;
54
- }
55
- /**
56
- * Options for `AuthManager.restoreFromCookies()` / `AuthManager.initialize()`.
57
- */
58
- export interface RestoreFromCookiesOptions {
59
- /**
60
- * Abort the underlying `POST /auth/refresh-all` after this many milliseconds
61
- * and treat it as "no signed-in accounts" instead of hanging. Forwarded
62
- * verbatim to `OxyServices.refreshAllSessions({ timeout })`.
63
- *
64
- * Intended for the cold-boot cookie-restore step on a cross-domain RP, where
65
- * the `Domain=oxy.so` refresh cookie never reaches `api.<apex>` and the
66
- * request can stall with no useful answer. Omit (the default) to wait
67
- * indefinitely — the warm cross-tab cascade path passes nothing, preserving
68
- * its existing behaviour.
69
- */
70
- timeout?: number;
71
- }
72
- /**
73
- * Outcome of `AuthManager.switchAuthuser()`.
74
- *
75
- * Mirrors the wire `RefreshCookieResponse`.
76
- */
77
- export interface SwitchAuthuserResult {
78
- accessToken: string;
79
- expiresAt: string;
80
- authuser: number;
81
- }
@@ -1,164 +0,0 @@
1
- /**
2
- * Cross-Domain Authentication Helper
3
- *
4
- * Provides a simplified API for cross-domain SSO authentication. The
5
- * automatic sign-in path uses a full-page redirect through the central IdP
6
- * (`auth.oxy.so`) — a tokenless, universal mechanism that works in every
7
- * browser.
8
- *
9
- * FedCM (`signInWithFedCM`) is intentionally NOT part of the automatic
10
- * (`'auto'`) path: it is a Chrome-only browser API, and a misconfigured or
11
- * unreachable FedCM endpoint fails fast and silently, which — combined with a
12
- * caller's auth-guard effect re-invoking `signIn()` whenever the user is still
13
- * unauthenticated — produced a real production incident (an accelerating
14
- * `autoSignIn` → FedCM-fails → redirect retry loop). `signInWithFedCM` remains
15
- * available for callers that want to opt into it EXPLICITLY
16
- * (`signIn({ method: 'fedcm' })`).
17
- *
18
- * Usage:
19
- * ```typescript
20
- * import { CrossDomainAuth } from '@oxyhq/core';
21
- *
22
- * const auth = new CrossDomainAuth(oxyServices);
23
- *
24
- * // Automatic method selection (always redirect)
25
- * const session = await auth.signIn();
26
- *
27
- * // Or use a specific method
28
- * auth.signInWithRedirect();
29
- * ```
30
- */
31
- import type { OxyServices } from './OxyServices';
32
- import type { SessionLoginResponse } from './models/session';
33
- export interface CrossDomainAuthOptions {
34
- /**
35
- * Preferred authentication method
36
- * - 'auto': Automatically select best method (default)
37
- * - 'fedcm': Use FedCM (browser-native)
38
- * - 'redirect': Use full-page redirect
39
- */
40
- method?: 'auto' | 'fedcm' | 'redirect';
41
- /**
42
- * Custom redirect URI (for redirect method)
43
- */
44
- redirectUri?: string;
45
- /**
46
- * Whether to open signup page instead of login
47
- */
48
- isSignup?: boolean;
49
- /**
50
- * Callback when auth method is selected
51
- */
52
- onMethodSelected?: (method: 'fedcm' | 'redirect') => void;
53
- }
54
- export declare class CrossDomainAuth {
55
- private oxyServices;
56
- constructor(oxyServices: OxyServices);
57
- /**
58
- * Sign in with automatic method selection.
59
- *
60
- * Auto mode always uses the full-page redirect (see the class doc comment
61
- * for why FedCM was removed from this path). Pass `{ method: 'fedcm' }` to
62
- * opt into FedCM explicitly.
63
- *
64
- * @param options - Authentication options
65
- * @returns Session with user data and access token
66
- */
67
- signIn(options?: CrossDomainAuthOptions): Promise<SessionLoginResponse | null>;
68
- /**
69
- * Automatic sign-in.
70
- *
71
- * Goes straight to the full-page redirect — the sole automatic method.
72
- * FedCM is deliberately NOT attempted here (see the class doc comment):
73
- * it is Chrome-only, and its fast/silent failure mode combined with a
74
- * caller's auth-guard effect re-invoking `signIn()` produced a real
75
- * production sign-in loop. Use `signIn({ method: 'fedcm' })` to opt in
76
- * explicitly.
77
- *
78
- * @private
79
- */
80
- private autoSignIn;
81
- /**
82
- * Sign in using FedCM (Federated Credential Management)
83
- *
84
- * Best method - browser-native, Google-like experience
85
- */
86
- signInWithFedCM(options?: CrossDomainAuthOptions): Promise<SessionLoginResponse>;
87
- /**
88
- * Sign in using full-page redirect
89
- *
90
- * Fallback method - works everywhere but loses app state
91
- */
92
- signInWithRedirect(options?: CrossDomainAuthOptions): void;
93
- /**
94
- * Handle redirect callback
95
- *
96
- * Call this on app startup to check if we're returning from auth redirect
97
- */
98
- handleRedirectCallback(): SessionLoginResponse | null;
99
- /**
100
- * Silent sign-in (check for existing session)
101
- *
102
- * Tries to automatically sign in without user interaction, via the
103
- * iframe-based silent auth against the per-apex `/auth/silent` IdP host.
104
- * FedCM is deliberately NOT attempted here (see the class doc comment).
105
- *
106
- * @returns Session if user is already signed in, null otherwise
107
- */
108
- silentSignIn(): Promise<SessionLoginResponse | null>;
109
- /**
110
- * Restore session from storage.
111
- *
112
- * Access tokens are no longer persisted in browser storage; providers restore
113
- * through refresh cookies / SSO code exchange instead.
114
- */
115
- restoreSession(): boolean;
116
- /**
117
- * Check if FedCM is supported in current browser
118
- */
119
- isFedCMSupported(): boolean;
120
- /**
121
- * Get recommended authentication method for current environment
122
- *
123
- * Redirect is the sole recommended automatic method — it works in every
124
- * browser, unlike FedCM (Chrome-only). Callers that want FedCM must opt in
125
- * explicitly via `signIn({ method: 'fedcm' })`.
126
- *
127
- * @returns Recommended method name and reason
128
- */
129
- getRecommendedMethod(): {
130
- method: 'redirect';
131
- reason: string;
132
- };
133
- /**
134
- * Initialize cross-domain auth on app startup
135
- *
136
- * This handles:
137
- * 1. Redirect callback (if returning from auth.oxy.so)
138
- * 2. Silent sign-in (check for existing SSO session)
139
- *
140
- * @returns Session if user is authenticated, null otherwise
141
- */
142
- initialize(): Promise<SessionLoginResponse | null>;
143
- }
144
- /**
145
- * Helper function to create CrossDomainAuth instance
146
- *
147
- * @example
148
- * ```typescript
149
- * import { createCrossDomainAuth } from '@oxyhq/core';
150
- *
151
- * const oxyServices = new OxyServices({ baseURL: 'https://api.oxy.so' });
152
- * const auth = createCrossDomainAuth(oxyServices);
153
- *
154
- * // On app startup
155
- * const session = await auth.initialize();
156
- * if (session) {
157
- * console.log('User is signed in:', session.user);
158
- * }
159
- *
160
- * // Sign in button click
161
- * const session = await auth.signIn();
162
- * ```
163
- */
164
- export declare function createCrossDomainAuth(oxyServices: OxyServices): CrossDomainAuth;
@@ -1,331 +0,0 @@
1
- import type { OxyServicesBase } from '../OxyServices.base';
2
- import type { SessionLoginResponse } from '../models/session';
3
- export interface FedCMAuthOptions {
4
- nonce?: string;
5
- context?: 'signin' | 'signup' | 'continue' | 'use';
6
- loginHint?: string;
7
- }
8
- export interface FedCMConfig {
9
- enabled: boolean;
10
- configURL: string;
11
- clientId?: string;
12
- }
13
- /**
14
- * FedCM request mode values.
15
- *
16
- * The W3C FedCM spec renamed the `IdentityCredentialRequestOptions.mode` enum:
17
- * `'widget'` → `'passive'` and `'button'` → `'active'`. Modern Chrome only
18
- * accepts `'active'`/`'passive'` and throws a synchronous `TypeError` for the
19
- * legacy values, while Chrome 125–131 only understands `'button'`/`'widget'`.
20
- * Callers should use the modern values; the legacy values are accepted for
21
- * convenience and normalised internally.
22
- */
23
- export type FedCMRequestMode = 'active' | 'passive' | 'button' | 'widget';
24
- /**
25
- * Normalised result of a FedCM credential request: the IdP-issued ID token plus
26
- * whether the browser auto-selected the account (no explicit user choice).
27
- */
28
- interface FedCMTokenResult {
29
- token: string;
30
- isAutoSelected: boolean;
31
- }
32
- /**
33
- * Federated Credential Management (FedCM) Authentication Mixin
34
- *
35
- * Implements the modern browser-native identity federation API that enables
36
- * Google-style cross-domain authentication without third-party cookies.
37
- *
38
- * Browser Support:
39
- * - Chrome 108+
40
- * - Safari 16.4+
41
- * - Edge 108+
42
- * - Firefox: Not yet supported (fallback required)
43
- *
44
- * Key Features:
45
- * - No redirects or secondary windows required
46
- * - Browser-native UI prompts
47
- * - Privacy-preserving (IdP can't track users)
48
- * - Automatic SSO across domains
49
- * - Silent re-authentication support
50
- *
51
- * @see https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API
52
- */
53
- export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T): {
54
- new (...args: any[]): {
55
- resolveFedcmConfigUrl(): string;
56
- /**
57
- * Instance method to check FedCM support
58
- */
59
- isFedCMSupported(): boolean;
60
- /**
61
- * Sign in using FedCM (Federated Credential Management API)
62
- *
63
- * This provides a Google-style authentication experience:
64
- * - Browser shows native "Sign in with Oxy" prompt
65
- * - No redirect or secondary window required
66
- * - User approves → credential exchange happens in browser
67
- * - All apps automatically get SSO after first sign-in
68
- *
69
- * @param options - Authentication options
70
- * @returns Session with access token and user data
71
- * @throws {OxyAuthenticationError} If FedCM not supported or user cancels
72
- *
73
- * @example
74
- * ```typescript
75
- * try {
76
- * const session = await oxyServices.signInWithFedCM();
77
- * const user = session.user;
78
- * } catch (error) {
79
- * // Fallback to redirect auth
80
- * oxyServices.signInWithRedirect();
81
- * }
82
- * ```
83
- */
84
- signInWithFedCM(options?: FedCMAuthOptions): Promise<SessionLoginResponse>;
85
- /**
86
- * Run a single interactive FedCM credential request + token exchange for the
87
- * given (possibly undefined) loginHint. A successful exchange plants the
88
- * access token and persists the user id as the future loginHint — the hint is
89
- * therefore only ever stored after a GENUINELY successful sign-in, never
90
- * speculatively.
91
- *
92
- * @private
93
- */
94
- attemptInteractiveSignIn(options: FedCMAuthOptions, loginHint: string | undefined): Promise<SessionLoginResponse>;
95
- /**
96
- * Map a raw FedCM/exchange failure to a user-facing {@link OxyAuthenticationError}
97
- * (or pass it through). Extracted so the clear-and-retry path can reuse the
98
- * exact same error normalisation as the first attempt.
99
- *
100
- * @private
101
- */
102
- normalizeInteractiveSignInError(error: unknown): unknown;
103
- /**
104
- * Silent sign-in using FedCM
105
- *
106
- * Attempts to automatically re-authenticate the user without any UI.
107
- * This is what enables "instant sign-in" across all Oxy domains after
108
- * the user has signed in once.
109
- *
110
- * The browser will:
111
- * 1. Check if user has previously signed in to Oxy
112
- * 2. Check if user is still signed in at auth.oxy.so
113
- * 3. If yes, automatically provide credential without prompting
114
- *
115
- * @returns Session if user is already signed in, null otherwise
116
- *
117
- * @example
118
- * ```typescript
119
- * // On app startup
120
- * useEffect(() => {
121
- * const checkAuth = async () => {
122
- * const session = await oxyServices.silentSignInWithFedCM();
123
- * if (session) {
124
- * setUser(session.user);
125
- * } else {
126
- * // Show sign-in button
127
- * }
128
- * };
129
- * checkAuth();
130
- * }, []);
131
- * ```
132
- */
133
- silentSignInWithFedCM(): Promise<SessionLoginResponse | null>;
134
- /**
135
- * Request identity credential from browser using FedCM API
136
- *
137
- * Uses a global lock to prevent concurrent requests, as FedCM only
138
- * allows one navigator.credentials.get request at a time.
139
- *
140
- * Interactive requests (optional/required) wait for any silent request to finish first.
141
- *
142
- * @private
143
- */
144
- requestIdentityCredential(options: {
145
- configURL: string;
146
- clientId: string;
147
- nonce: string;
148
- context?: string;
149
- loginHint?: string;
150
- mediation?: "silent" | "optional" | "required";
151
- /**
152
- * FedCM request mode. The W3C spec values are `'active'` (user-gesture
153
- * button flow) and `'passive'` (browser-initiated widget flow). Chrome
154
- * 125–131 used the legacy names `'button'`/`'widget'`; those are accepted
155
- * here and mapped to the modern values, with an automatic legacy retry if
156
- * the running browser only understands the old enum.
157
- */
158
- mode?: FedCMRequestMode;
159
- }): Promise<FedCMTokenResult | null>;
160
- /**
161
- * Exchange FedCM ID token for Oxy session
162
- *
163
- * The ID token is a JWT issued by auth.oxy.so that proves the user's
164
- * identity. We exchange it for a full Oxy session with access token.
165
- *
166
- * @private
167
- */
168
- exchangeIdTokenForSession(idToken: string): Promise<SessionLoginResponse>;
169
- /**
170
- * Revoke FedCM credential (sign out)
171
- *
172
- * Uses IdentityCredential.disconnect() to tell the browser to forget
173
- * the RP-IdP-account association. This resets the "returning account"
174
- * state, which is required for silent mediation to work again.
175
- */
176
- revokeFedCMCredential(): Promise<void>;
177
- /**
178
- * Get configuration for FedCM
179
- *
180
- * @returns FedCM configuration with browser support info
181
- */
182
- getFedCMConfig(): FedCMConfig;
183
- /**
184
- * Generate a cryptographically secure local nonce for FedCM.
185
- *
186
- * NOTE: this is a *local* fallback only. The server-side `/fedcm/exchange`
187
- * endpoint requires the nonce embedded in the ID token to have been minted
188
- * by `POST /fedcm/nonce` (see {@link mintServerNonce}) and bound to this
189
- * origin. A purely local nonce will be rejected with `invalid_nonce`. Use
190
- * {@link getFedcmNonce}, which prefers a server-minted nonce and only falls
191
- * back to this generator when the mint endpoint is unreachable.
192
- *
193
- * @private
194
- */
195
- generateNonce(): string;
196
- /**
197
- * Mint a single-use, origin-bound nonce from the Oxy API.
198
- *
199
- * The FedCM ID token issued by the IdP embeds this nonce as the `nonce`
200
- * claim. When the consuming app calls `POST /fedcm/exchange`, the API burns
201
- * the nonce (atomic `usedAt` transition) and verifies it was minted for the
202
- * same origin as the token `aud`. This is the anti-replay binding required
203
- * by the API's H9 hardening — without a server-minted nonce the exchange
204
- * always fails.
205
- *
206
- * The browser attaches the `Origin` header automatically on this
207
- * cross-origin request, so the API binds the nonce to the calling app's
208
- * origin (which also becomes the FedCM `clientId`/token `aud`).
209
- *
210
- * @private
211
- */
212
- mintServerNonce(): Promise<string>;
213
- /**
214
- * Resolve the nonce to use for a FedCM credential request.
215
- *
216
- * Prefers a server-minted, origin-bound nonce (required for the token
217
- * exchange to succeed). If the mint endpoint is unreachable we fall back to
218
- * a locally generated nonce so the browser flow can still proceed; the
219
- * exchange may then fail server-side, but that is strictly better than
220
- * throwing before the browser ever shows its UI.
221
- *
222
- * @private
223
- */
224
- getFedcmNonce(): Promise<string>;
225
- /**
226
- * Get the client ID for this origin
227
- *
228
- * @private
229
- */
230
- getClientId(): string;
231
- /** @internal */
232
- getStoredLoginHint(): string | undefined;
233
- /** @internal */
234
- storeLoginHint(userId: string): void;
235
- /** @internal */
236
- clearLoginHint(): void;
237
- /**
238
- * List the authenticated user's authorized RP apps.
239
- *
240
- * Returns the intersection of the user's FedCM grants and the currently-
241
- * approved RP catalog — what powers the "Connected apps" management UI in
242
- * @oxyhq/services. Requires a real user session; service tokens are
243
- * rejected by the underlying endpoint.
244
- */
245
- listAuthorizedApps(): Promise<AuthorizedApp[]>;
246
- /**
247
- * Revoke the authenticated user's authorization for a specific RP origin.
248
- *
249
- * The next FedCM sign-in from that origin will require explicit re-consent.
250
- * The corresponding cache entry is invalidated so a subsequent
251
- * `listAuthorizedApps()` call sees fresh data.
252
- */
253
- revokeAuthorizedApp(origin: string): Promise<void>;
254
- httpService: import("../HttpService").HttpService;
255
- cloudURL: string;
256
- config: import("../OxyServices.base").OxyConfig;
257
- __resetTokensForTests(): void;
258
- makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
259
- getBaseURL(): string;
260
- getSessionBaseUrl(): string;
261
- getClient(): import("../HttpService").HttpService;
262
- createLinkedClient(config: import("../OxyServices.base").OxyConfig): import("..").LinkedHttpClient;
263
- getMetrics(): {
264
- totalRequests: number;
265
- successfulRequests: number;
266
- failedRequests: number;
267
- cacheHits: number;
268
- cacheMisses: number;
269
- averageResponseTime: number;
270
- };
271
- clearCache(): void;
272
- clearCacheEntry(key: string): void;
273
- clearCacheByPrefix(prefix: string): number;
274
- getCacheStats(): {
275
- size: number;
276
- hits: number;
277
- misses: number;
278
- hitRate: number;
279
- };
280
- getCloudURL(): string;
281
- setTokens(accessToken: string): void;
282
- clearTokens(): void;
283
- onTokensChanged(listener: (accessToken: string | null) => void): () => void;
284
- _cachedUserId: string | null | undefined;
285
- _cachedAccessToken: string | null;
286
- getCurrentUserId(): string | null;
287
- hasValidToken(): boolean;
288
- getAccessToken(): string | null;
289
- establishDeviceRefreshSlot(): Promise<number | null>;
290
- getAccessTokenExpiry(): number | null;
291
- waitForAuth(timeoutMs?: number): Promise<boolean>;
292
- withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
293
- maxRetries?: number;
294
- retryDelay?: number;
295
- authTimeoutMs?: number;
296
- }): Promise<T_1>;
297
- validate(): Promise<boolean>;
298
- handleError(error: unknown): Error;
299
- healthCheck(): Promise<{
300
- status: string;
301
- users?: number;
302
- timestamp?: string;
303
- [key: string]: any;
304
- }>;
305
- };
306
- readonly DEFAULT_CONFIG_URL: "https://auth.oxy.so/fedcm.json";
307
- readonly FEDCM_TIMEOUT: 15000;
308
- readonly FEDCM_SILENT_TIMEOUT: 4000;
309
- readonly FEDCM_ABORT_SETTLE_GRACE_MS: 500;
310
- /**
311
- * Check if FedCM is supported in the current browser
312
- */
313
- isFedCMSupported(): boolean;
314
- } & T;
315
- /**
316
- * Public summary of an RP application the user has authorized — mirrors the
317
- * `AuthorizedAppSummary` shape returned by `GET /fedcm/me/authorized-apps`.
318
- */
319
- export interface AuthorizedApp {
320
- /** Normalised RP origin. */
321
- origin: string;
322
- /** Friendly display name. */
323
- name: string;
324
- /** Optional human-readable description. */
325
- description?: string;
326
- /** ISO-8601 timestamp of when the user first authorized this RP. */
327
- firstGrantedAt: string;
328
- /** ISO-8601 timestamp of the most recent FedCM exchange for this user+RP. */
329
- lastUsedAt: string;
330
- }
331
- export { OxyServicesFedCMMixin as FedCMMixin };