1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

[EC-598] feat: add support for user verifiction using MP during assertion

This commit is contained in:
Andreas Coroiu
2023-04-20 16:34:53 +02:00
parent 757050430d
commit 3a1b56860e
6 changed files with 60 additions and 60 deletions

View File

@@ -4,6 +4,11 @@ export interface NewCredentialParams {
userVerification: boolean;
}
export interface PickCredentialParams {
cipherIds: string[];
userVerification: boolean;
}
export abstract class Fido2UserInterfaceService {
newSession: (abortController?: AbortController) => Promise<Fido2UserInterfaceSession>;
}
@@ -12,8 +17,10 @@ export abstract class Fido2UserInterfaceSession {
fallbackRequested = false;
aborted = false;
confirmCredential: (cipherId: string, abortController?: AbortController) => Promise<boolean>;
pickCredential: (cipherIds: string[], abortController?: AbortController) => Promise<string>;
pickCredential: (
params: PickCredentialParams,
abortController?: AbortController
) => Promise<{ cipherId: string; userVerified: boolean }>;
confirmNewCredential: (
params: NewCredentialParams,
abortController?: AbortController

View File

@@ -716,18 +716,30 @@ describe("FidoAuthenticatorService", () => {
cipherService.getAllDecrypted.mockResolvedValue(ciphers);
});
/** Spec: Prompt the user to select a public key credential source selectedCredential from credentialOptions. */
it("should request confirmation from the user", async () => {
userInterfaceSession.pickCredential.mockResolvedValue(ciphers[0].id);
for (const userVerification of [true, false]) {
/** Spec: Prompt the user to select a public key credential source selectedCredential from credentialOptions. */
it(`should request confirmation from user when user verification is ${userVerification}`, async () => {
params.requireUserVerification = userVerification;
userInterfaceSession.pickCredential.mockResolvedValue({
cipherId: ciphers[0].id,
userVerified: userVerification,
});
await authenticator.getAssertion(params);
await authenticator.getAssertion(params);
expect(userInterfaceSession.pickCredential).toHaveBeenCalledWith(ciphers.map((c) => c.id));
});
expect(userInterfaceSession.pickCredential).toHaveBeenCalledWith({
cipherIds: ciphers.map((c) => c.id),
userVerification,
});
});
}
/** Spec: If the user does not consent, return an error code equivalent to "NotAllowedError" and terminate the operation. */
it("should throw error", async () => {
userInterfaceSession.pickCredential.mockResolvedValue(undefined);
userInterfaceSession.pickCredential.mockResolvedValue({
cipherId: undefined,
userVerified: false,
});
const result = async () => await authenticator.getAssertion(params);
@@ -783,7 +795,10 @@ describe("FidoAuthenticatorService", () => {
});
}
cipherService.getAllDecrypted.mockResolvedValue(ciphers);
userInterfaceSession.pickCredential.mockResolvedValue(ciphers[0].id);
userInterfaceSession.pickCredential.mockResolvedValue({
cipherId: ciphers[0].id,
userVerified: false,
});
};
beforeEach(init);

View File

@@ -202,15 +202,22 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
}
const selectedCipherId = await userInterfaceSession.pickCredential(
cipherOptions.map((cipher) => cipher.id)
);
const response = await userInterfaceSession.pickCredential({
cipherIds: cipherOptions.map((cipher) => cipher.id),
userVerification: params.requireUserVerification,
});
const selectedCipherId = response.cipherId;
const userVerified = response.userVerified;
const selectedCipher = cipherOptions.find((c) => c.id === selectedCipherId);
if (selectedCipher === undefined) {
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
}
if (params.requireUserVerification && !userVerified) {
throw new Fido2AutenticatorError(Fido2AutenticatorErrorCode.NotAllowed);
}
try {
const selectedFido2Key =
selectedCipher.type === CipherType.Login
@@ -235,7 +242,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
credentialId: Utils.guidToRawFormat(selectedCredentialId),
counter: selectedFido2Key.counter,
userPresence: true,
userVerification: false,
userVerification: userVerified,
});
const signature = await generateSignature({