mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 17:23:37 +00:00
[EC-598] feat: allow user to pick which credential to use
This commit is contained in:
@@ -3,7 +3,6 @@ export interface NewCredentialParams {
|
||||
}
|
||||
|
||||
export abstract class Fido2UserInterfaceService {
|
||||
verifyUser: () => Promise<boolean>;
|
||||
verifyPresence: () => Promise<boolean>;
|
||||
pickCredential: (cipherIds: string[]) => Promise<string>;
|
||||
confirmNewCredential: (params: NewCredentialParams) => Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -120,21 +120,30 @@ export class Fido2Service implements Fido2ServiceAbstraction {
|
||||
if (params.allowedCredentialIds && params.allowedCredentialIds.length > 0) {
|
||||
// We're looking for regular non-resident keys
|
||||
credential = await this.getCredential(params.allowedCredentialIds);
|
||||
|
||||
if (credential === undefined) {
|
||||
throw new NoCredentialFoundError();
|
||||
}
|
||||
|
||||
if (credential.origin !== params.origin) {
|
||||
throw new OriginMismatchError();
|
||||
}
|
||||
|
||||
await this.fido2UserInterfaceService.pickCredential([credential.credentialId.encoded]);
|
||||
} else {
|
||||
// We're looking for a resident key
|
||||
credential = await this.getCredentialByRp(params.rpId);
|
||||
}
|
||||
const credentials = await this.getCredentialsByRp(params.rpId);
|
||||
|
||||
if (credential === undefined) {
|
||||
throw new NoCredentialFoundError();
|
||||
}
|
||||
if (credentials.length === 0) {
|
||||
throw new NoCredentialFoundError();
|
||||
}
|
||||
|
||||
if (credential.origin !== params.origin) {
|
||||
throw new OriginMismatchError();
|
||||
const pickedId = await this.fido2UserInterfaceService.pickCredential(
|
||||
credentials.map((c) => c.credentialId.encoded)
|
||||
);
|
||||
credential = credentials.find((c) => c.credentialId.encoded === pickedId);
|
||||
}
|
||||
|
||||
const presence = await this.fido2UserInterfaceService.verifyPresence();
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const clientData = encoder.encode(
|
||||
JSON.stringify({
|
||||
@@ -147,7 +156,7 @@ export class Fido2Service implements Fido2ServiceAbstraction {
|
||||
const authData = await generateAuthData({
|
||||
credentialId: credential.credentialId,
|
||||
rpId: params.rpId,
|
||||
userPresence: presence,
|
||||
userPresence: true,
|
||||
userVerification: true, // TODO: Change to false!
|
||||
});
|
||||
|
||||
@@ -171,7 +180,7 @@ export class Fido2Service implements Fido2ServiceAbstraction {
|
||||
for (const allowedCredential of allowedCredentialIds) {
|
||||
cipher = await this.cipherService.get(allowedCredential);
|
||||
|
||||
if (cipher.deletedDate != undefined) {
|
||||
if (cipher?.deletedDate != undefined) {
|
||||
cipher = undefined;
|
||||
}
|
||||
|
||||
@@ -209,17 +218,13 @@ export class Fido2Service implements Fido2ServiceAbstraction {
|
||||
return new CredentialId(cipher.id);
|
||||
}
|
||||
|
||||
private async getCredentialByRp(rpId: string): Promise<BitCredential | undefined> {
|
||||
private async getCredentialsByRp(rpId: string): Promise<BitCredential[]> {
|
||||
const allCipherViews = await this.cipherService.getAllDecrypted();
|
||||
const cipherView = allCipherViews.find(
|
||||
const cipherViews = allCipherViews.filter(
|
||||
(cv) => !cv.isDeleted && cv.type === CipherType.Fido2Key && cv.fido2Key?.rpId === rpId
|
||||
);
|
||||
|
||||
if (cipherView == undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await mapCipherViewToBitCredential(cipherView);
|
||||
return await Promise.all(cipherViews.map((view) => mapCipherViewToBitCredential(view)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "../../abstractions/fido2/fido2-user-interface.service.abstraction";
|
||||
import { RequestAbortedError } from "../../abstractions/fido2/fido2.service.abstraction";
|
||||
|
||||
export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
|
||||
async verifyUser(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async verifyPresence(): Promise<boolean> {
|
||||
return false;
|
||||
pickCredential(cipherIds: string[]): Promise<string> {
|
||||
throw new RequestAbortedError();
|
||||
}
|
||||
|
||||
async confirmNewCredential(): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user