mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[EC-598] feat: implement missing credential checks
This commit is contained in:
@@ -537,14 +537,49 @@ describe("FidoAuthenticatorService", () => {
|
||||
});
|
||||
|
||||
describe("vault is missing non-discoverable credential", () => {
|
||||
let excludedId: string;
|
||||
let params: Fido2AuthenticatorGetAssertionParams;
|
||||
|
||||
beforeEach(async () => {
|
||||
excludedId = Utils.newGuid();
|
||||
params = await createParams({
|
||||
allowCredentialDescriptorList: [
|
||||
{ id: Utils.guidToRawFormat(excludedId), type: "public-key" },
|
||||
],
|
||||
rpId: RpId,
|
||||
});
|
||||
});
|
||||
|
||||
/** Spec: If credentialOptions is now empty, return an error code equivalent to "NotAllowedError" and terminate the operation. */
|
||||
it("should throw error if no credential exists", async () => {
|
||||
cipherService.getAllDecrypted.mockResolvedValue([]);
|
||||
|
||||
const result = async () => await authenticator.getAssertion(params);
|
||||
|
||||
await expect(result).rejects.toThrowError(Fido2AutenticatorErrorCode.NotAllowed);
|
||||
});
|
||||
|
||||
it("should throw error if credential exists but rpId does not match", async () => {
|
||||
const cipher = await createCipher({ type: CipherType.Login }).decrypt();
|
||||
cipher.fido2Key.nonDiscoverableId = excludedId;
|
||||
cipher.fido2Key.rpId = "mismatch-rpid";
|
||||
cipherService.getAllDecrypted.mockResolvedValue([cipher]);
|
||||
|
||||
const result = async () => await authenticator.getAssertion(params);
|
||||
|
||||
await expect(result).rejects.toThrowError(Fido2AutenticatorErrorCode.NotAllowed);
|
||||
});
|
||||
});
|
||||
|
||||
describe("vault is missing discoverable credential", () => {
|
||||
let params: Fido2AuthenticatorGetAssertionParams;
|
||||
|
||||
beforeEach(async () => {
|
||||
params = await createParams({
|
||||
allowCredentialDescriptorList: [
|
||||
{ id: Utils.guidToRawFormat(Utils.newGuid()), type: "public-key" },
|
||||
],
|
||||
allowCredentialDescriptorList: [],
|
||||
rpId: RpId,
|
||||
});
|
||||
cipherService.getAllDecrypted.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
/** Spec: If credentialOptions is now empty, return an error code equivalent to "NotAllowedError" and terminate the operation. */
|
||||
|
||||
@@ -146,6 +146,24 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.Constraint);
|
||||
}
|
||||
|
||||
let credentialOptions: Fido2KeyView[];
|
||||
|
||||
// eslint-disable-next-line no-empty
|
||||
if (params.allowCredentialDescriptorList?.length > 0) {
|
||||
const ciphers = await this.findNonDiscoverableCredentials(
|
||||
params.allowCredentialDescriptorList,
|
||||
params.rpId
|
||||
);
|
||||
credentialOptions = ciphers.map((c) => c.fido2Key);
|
||||
} else {
|
||||
const ciphers = await this.findDiscoverableCredentials(params.rpId);
|
||||
credentialOptions = ciphers.map((c) => c.fido2Key);
|
||||
}
|
||||
|
||||
if (credentialOptions.length === 0) {
|
||||
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
|
||||
}
|
||||
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
@@ -175,6 +193,40 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
);
|
||||
}
|
||||
|
||||
private async findNonDiscoverableCredentials(
|
||||
credentials: PublicKeyCredentialDescriptor[],
|
||||
rpId: string
|
||||
): Promise<CipherView[]> {
|
||||
const ids: string[] = [];
|
||||
|
||||
for (const credential of credentials) {
|
||||
try {
|
||||
ids.push(Utils.guidToStandardFormat(credential.id));
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (ids.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ciphers = await this.cipherService.getAllDecrypted();
|
||||
return ciphers.filter(
|
||||
(cipher) =>
|
||||
cipher.type === CipherType.Login &&
|
||||
cipher.fido2Key != undefined &&
|
||||
cipher.fido2Key.rpId === rpId &&
|
||||
ids.includes(cipher.fido2Key.nonDiscoverableId)
|
||||
);
|
||||
}
|
||||
|
||||
private async findDiscoverableCredentials(rpId: string): Promise<CipherView[]> {
|
||||
const ciphers = await this.cipherService.getAllDecrypted();
|
||||
return ciphers.filter(
|
||||
(cipher) => cipher.type === CipherType.Fido2Key && cipher.fido2Key.rpId === rpId
|
||||
);
|
||||
}
|
||||
|
||||
private async createKeyPair() {
|
||||
return await crypto.subtle.generateKey(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user