Skip to content

Commit bf3cde0

Browse files
committed
FB: ser
1 parent 2197be7 commit bf3cde0

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

packages/connect-react/src/types/flags.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ export class Flags {
1212
addFlags(items: Record<string, string>) {
1313
for (const [name, value] of Object.entries(items)) {
1414
this.items[name] = value;
15+
16+
if (
17+
name === keyConditionalUI &&
18+
value === 'false' &&
19+
this.items[keyEventLow] &&
20+
this.items[keyEventLow] === 'true'
21+
) {
22+
this.items[keyEventLow] = 'false';
23+
}
1524
}
1625
}
1726

packages/web-core/src/services/WebAuthnService.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,22 @@ export class WebAuthnService {
7474
} as never)) as PublicKeyCredential;
7575
}
7676

77+
if (WebAuthnService.isInjectedCredential(credential) || !credential.toJSON) {
78+
return {
79+
response: JSON.stringify(createResponseToJSON(credential)),
80+
message: 'using fallback serializer (extension-injected credential)',
81+
};
82+
}
83+
7784
try {
78-
console.log('credential', credential);
7985
return {
8086
response: JSON.stringify(credential.toJSON()),
8187
message: '',
8288
};
8389
} catch (e) {
8490
return {
8591
response: JSON.stringify(createResponseToJSON(credential)),
86-
message: 'toJSON() not available on PublicKeyCredential',
92+
message: 'toJSON() threw on PublicKeyCredential',
8793
};
8894
}
8995
}
@@ -134,6 +140,13 @@ export class WebAuthnService {
134140
signal: abortController.signal,
135141
})) as PublicKeyCredential;
136142

143+
if (WebAuthnService.isInjectedCredential(credential) || !credential.toJSON) {
144+
return {
145+
response: JSON.stringify(getResponseToJSON(credential)),
146+
message: 'using fallback serializer (extension-injected credential)',
147+
};
148+
}
149+
137150
try {
138151
return {
139152
response: JSON.stringify(credential.toJSON()),
@@ -142,7 +155,7 @@ export class WebAuthnService {
142155
} catch (e) {
143156
return {
144157
response: JSON.stringify(getResponseToJSON(credential)),
145-
message: 'toJSON() not available on PublicKeyCredential',
158+
message: 'toJSON() threw on PublicKeyCredential',
146159
};
147160
}
148161
}
@@ -339,6 +352,32 @@ export class WebAuthnService {
339352
}
340353
}
341354

355+
/**
356+
* Detects whether a `PublicKeyCredential` was returned by a browser-extension
357+
* password manager (e.g. Dashlane, 1Password, Bitwarden) rather than the
358+
* browser's native WebAuthn implementation.
359+
*
360+
* Such extensions monkey-patch `navigator.credentials.get/create` and return
361+
* an object that lacks the WebAuthn internal slots. Calling the native
362+
* `PublicKeyCredential.prototype.toJSON()` on these objects either throws
363+
* `TypeError: Illegal invocation` or silently returns empty buffers
364+
* (`clientDataJSON`, `attestationObject`, `signature`, ...).
365+
*
366+
* Heuristic: real credentials inherit `getClientExtensionResults` from the
367+
* prototype; injected ones replace it with an own function.
368+
*
369+
* See https://github.com/bitwarden/clients/issues/12060.
370+
*/
371+
static isInjectedCredential(credential: PublicKeyCredential): boolean {
372+
try {
373+
return (
374+
credential.getClientExtensionResults !== PublicKeyCredential.prototype.getClientExtensionResults
375+
);
376+
} catch (e) {
377+
return true;
378+
}
379+
}
380+
342381
static async raceWithTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
343382
const timeout = new Promise<never>((_, reject) =>
344383
setTimeout(() => reject(new ConnectError(ConnectErrorType.RaceTimeout, `timeout of ${ms}ms reached`)), ms),

0 commit comments

Comments
 (0)