@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.
- package/README.md +17 -2
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/mixins/OxyServices.fedcm.js +22 -9
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/mixins/OxyServices.fedcm.js +22 -9
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/mixins/OxyServices.fedcm.d.ts +9 -4
- package/package.json +1 -1
- package/src/mixins/OxyServices.fedcm.ts +52 -14
|
@@ -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
|
@@ -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<
|
|
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
|
|
228
|
-
|
|
229
|
-
|
|
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:
|
|
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 (
|
|
272
|
+
if (errorName === 'AbortError') {
|
|
245
273
|
throw new OxyAuthenticationError('Sign-in was cancelled by user');
|
|
246
274
|
}
|
|
247
|
-
if (
|
|
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:
|
|
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
|
-
|
|
373
|
-
|
|
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<
|
|
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
|
-
|
|
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
|
|
632
|
+
await identityCredential.disconnect({
|
|
595
633
|
configURL: this.resolveFedcmConfigUrl(),
|
|
596
634
|
clientId,
|
|
597
635
|
accountHint: accountHint || '*',
|