@oxyhq/core 1.11.19 → 1.11.20

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.
@@ -21,6 +21,14 @@ export interface FedCMConfig {
21
21
  * convenience and normalised internally.
22
22
  */
23
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
+ }
24
32
  /**
25
33
  * Federated Credential Management (FedCM) Authentication Mixin
26
34
  *
@@ -130,10 +138,7 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
130
138
  * the running browser only understands the old enum.
131
139
  */
132
140
  mode?: FedCMRequestMode;
133
- }): Promise<{
134
- token: string;
135
- isAutoSelected: boolean;
136
- } | null>;
141
+ }): Promise<FedCMTokenResult | null>;
137
142
  /**
138
143
  * Exchange FedCM ID token for Oxy session
139
144
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "1.11.19",
3
+ "version": "1.11.20",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -99,12 +99,35 @@ interface FedCMCredentialsContainer {
99
99
  get(options: FedCMCredentialRequest): Promise<FedCMIdentityCredential | null>;
100
100
  }
101
101
 
102
+ /**
103
+ * Normalised result of a FedCM credential request: the IdP-issued ID token plus
104
+ * whether the browser auto-selected the account (no explicit user choice).
105
+ */
106
+ interface FedCMTokenResult {
107
+ token: string;
108
+ isAutoSelected: boolean;
109
+ }
110
+
111
+ // Options accepted by the static `IdentityCredential.disconnect()` method
112
+ // (W3C FedCM "disconnect" / sign-out). Not declared in every lib-dom version.
113
+ interface FedCMDisconnectOptions {
114
+ configURL: string;
115
+ clientId: string;
116
+ accountHint: string;
117
+ }
118
+
119
+ // Minimal structural shape of the global `IdentityCredential` interface object,
120
+ // modelling only the static `disconnect` method this mixin invokes.
121
+ interface FedCMIdentityCredentialStatic {
122
+ disconnect(options: FedCMDisconnectOptions): Promise<void>;
123
+ }
124
+
102
125
  const FEDCM_LOGIN_HINT_KEY = 'oxy_fedcm_login_hint';
103
126
 
104
127
  // Global lock to prevent concurrent FedCM requests
105
128
  // FedCM only allows one navigator.credentials.get request at a time
106
129
  let fedCMRequestInProgress = false;
107
- let fedCMRequestPromise: Promise<any> | null = null;
130
+ let fedCMRequestPromise: Promise<FedCMTokenResult | null> | null = null;
108
131
  let currentMediationMode: string | null = null;
109
132
 
110
133
  /**
@@ -224,9 +247,11 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
224
247
  // Exchange FedCM ID token for Oxy session
225
248
  const session = await this.exchangeIdTokenForSession(credential.token);
226
249
 
227
- // Store access token in HttpService (extract from response or get from session)
228
- if (session && (session as any).accessToken) {
229
- this.httpService.setTokens((session as any).accessToken);
250
+ // Store access token in HttpService. `accessToken`/`refreshToken` are
251
+ // declared optional on SessionLoginResponse; default the refresh token to
252
+ // an empty string when the exchange did not return one.
253
+ if (session?.accessToken) {
254
+ this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
230
255
  }
231
256
 
232
257
  // Store the user ID as loginHint for future FedCM requests
@@ -234,17 +259,20 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
234
259
  this.storeLoginHint(session.user.id);
235
260
  }
236
261
 
237
- debug.log('Interactive sign-in: Success!', { userId: (session as any)?.user?.id });
262
+ debug.log('Interactive sign-in: Success!', { userId: session?.user?.id });
238
263
 
239
264
  return session;
240
265
  } catch (error) {
241
266
  debug.log('Interactive sign-in failed:', error);
242
267
  const errorMessage = error instanceof Error ? error.message : String(error);
268
+ // FedCM aborts/network failures surface as DOMException/Error instances,
269
+ // both of which carry a `name`. Anything else has no meaningful name.
270
+ const errorName = error instanceof Error ? error.name : '';
243
271
 
244
- if ((error as any).name === 'AbortError') {
272
+ if (errorName === 'AbortError') {
245
273
  throw new OxyAuthenticationError('Sign-in was cancelled by user');
246
274
  }
247
- if ((error as any).name === 'NetworkError') {
275
+ if (errorName === 'NetworkError') {
248
276
  throw new OxyAuthenticationError('Network error during sign-in. Please check your connection.');
249
277
  }
250
278
  if (errorMessage.includes('multiple accounts')) {
@@ -301,7 +329,7 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
301
329
  // We intentionally do NOT fall back to optional mediation here because
302
330
  // this runs on app startup — showing browser UI without user action is bad UX.
303
331
  // Optional/interactive mediation should only happen when the user clicks "Sign In".
304
- let credential: { token: string; isAutoSelected: boolean } | null = null;
332
+ let credential: FedCMTokenResult | null = null;
305
333
 
306
334
  const loginHint = this.getStoredLoginHint();
307
335
 
@@ -368,9 +396,11 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
368
396
  return null;
369
397
  }
370
398
 
371
- // Set the access token
372
- if ((session as any).accessToken) {
373
- this.httpService.setTokens((session as any).accessToken);
399
+ // Set the access token. `accessToken`/`refreshToken` are declared optional
400
+ // on SessionLoginResponse; default the refresh token to an empty string when
401
+ // the exchange did not return one.
402
+ if (session.accessToken) {
403
+ this.httpService.setTokens(session.accessToken, session.refreshToken ?? '');
374
404
  debug.log('Silent SSO: Access token set');
375
405
  } else {
376
406
  debug.warn('Silent SSO: No accessToken in session response');
@@ -414,7 +444,7 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
414
444
  * the running browser only understands the old enum.
415
445
  */
416
446
  mode?: FedCMRequestMode;
417
- }): Promise<{ token: string; isAutoSelected: boolean } | null> {
447
+ }): Promise<FedCMTokenResult | null> {
418
448
  const requestedMediation = options.mediation || 'optional';
419
449
  const isInteractive = requestedMediation !== 'silent';
420
450
 
@@ -589,9 +619,17 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
589
619
  }
590
620
 
591
621
  try {
592
- if ('IdentityCredential' in window && 'disconnect' in (window as any).IdentityCredential) {
622
+ // The DOM lib does not declare the global `IdentityCredential` interface
623
+ // object (with its static `disconnect`) in every TypeScript version we
624
+ // build against. Read it off `window` through the minimal structural type
625
+ // (not `any`), guarding that `disconnect` is actually present at runtime.
626
+ const fedCMWindow = window as unknown as {
627
+ IdentityCredential?: Partial<FedCMIdentityCredentialStatic>;
628
+ };
629
+ const identityCredential = fedCMWindow.IdentityCredential;
630
+ if (identityCredential && typeof identityCredential.disconnect === 'function') {
593
631
  const clientId = this.getClientId();
594
- await (window as any).IdentityCredential.disconnect({
632
+ await identityCredential.disconnect({
595
633
  configURL: this.resolveFedcmConfigUrl(),
596
634
  clientId,
597
635
  accountHint: accountHint || '*',