1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-16 10:45:20 +01:00

[EC-598] feat: differntiate between resident auth and 2fa

This commit is contained in:
Andreas Coroiu 2023-01-05 15:29:24 +01:00
parent 132c3fe04d
commit f7a74c8cf2
No known key found for this signature in database
GPG Key ID: E70B5FFC81DFEC1A
6 changed files with 66 additions and 9 deletions

View File

@ -1,7 +1,18 @@
<ng-container *ngIf="data">
<div class="auth-wrapper">
<ng-container *ngIf="data.type == 'ConfirmCredentialRequest'">
A site is asking for authentication using the following credential:
<div class="box list">
<div class="box-content">
<app-cipher-row [cipher]="ciphers[0]"></app-cipher-row>
</div>
</div>
<button type="button" class="btn btn-outline-secondary" (click)="confirm()">
Authenticate
</button>
</ng-container>
<ng-container *ngIf="data.type == 'PickCredentialRequest'">
A site is asking for authentication, please choose one of the following credentials to use
A site is asking for authentication, please choose one of the following credentials to use:
<div class="box list">
<div class="box-content">
<app-cipher-row
@ -11,10 +22,6 @@
></app-cipher-row>
</div>
</div>
<!-- <button type="button" class="btn btn-outline-secondary" (click)="accept()">
<ng-container *ngIf="data.type == 'VerifyUserRequest'">Authenticate</ng-container>
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">Create</ng-container>
</button> -->
</ng-container>
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">
A site wants to create the following passkey in your vault
@ -23,9 +30,7 @@
<app-cipher-row [cipher]="ciphers[0]"></app-cipher-row>
</div>
</div>
<button type="button" class="btn btn-outline-secondary" (click)="confirm()">
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">Create</ng-container>
</button>
<button type="button" class="btn btn-outline-secondary" (click)="confirmNew()">Create</button>
</ng-container>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(true)">
Use browser built-in

View File

@ -37,6 +37,9 @@ export class Fido2Component implements OnInit, OnDestroy {
cipher.type = CipherType.Fido2Key;
cipher.fido2Key = new Fido2KeyView();
this.ciphers = [cipher];
} else if (this.data?.type === "ConfirmCredentialRequest") {
const cipher = await this.cipherService.get(this.data.cipherId);
this.ciphers = [await cipher.decrypt()];
} else if (this.data?.type === "PickCredentialRequest") {
this.ciphers = await Promise.all(
this.data.cipherIds.map(async (cipherId) => {
@ -62,6 +65,14 @@ export class Fido2Component implements OnInit, OnDestroy {
}
confirm() {
BrowserFido2UserInterfaceService.sendMessage({
requestId: this.data.requestId,
type: "ConfirmCredentialResponse",
});
window.close();
}
confirmNew() {
BrowserFido2UserInterfaceService.sendMessage({
requestId: this.data.requestId,
type: "ConfirmNewCredentialResponse",

View File

@ -21,6 +21,13 @@ export type BrowserFido2Message = { requestId: string } & (
type: "PickCredentialResponse";
cipherId?: string;
}
| {
type: "ConfirmCredentialRequest";
cipherId: string;
}
| {
type: "ConfirmCredentialResponse";
}
| {
type: "ConfirmNewCredentialRequest";
name: string;
@ -50,6 +57,35 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
BrowserApi.messageListener(BrowserFido2MessageName, this.processMessage.bind(this));
}
async confirmCredential(cipherId: string): Promise<boolean> {
const requestId = Utils.newGuid();
const data: BrowserFido2Message = { type: "ConfirmCredentialRequest", cipherId, requestId };
const queryParams = new URLSearchParams({ data: JSON.stringify(data) }).toString();
this.popupUtilsService.popOut(
null,
`popup/index.html?uilocation=popout#/fido2?${queryParams}`,
{ center: true }
);
const response = await lastValueFrom(
this.messages$.pipe(
filter((msg) => msg.requestId === requestId),
first(),
takeUntil(this.destroy$)
)
);
if (response.type === "ConfirmCredentialResponse") {
return true;
}
if (response.type === "RequestCancelled") {
throw new RequestAbortedError(response.fallbackRequested);
}
return false;
}
async pickCredential(cipherIds: string[]): Promise<string> {
const requestId = Utils.newGuid();
const data: BrowserFido2Message = { type: "PickCredentialRequest", cipherIds, requestId };

View File

@ -3,6 +3,7 @@ export interface NewCredentialParams {
}
export abstract class Fido2UserInterfaceService {
confirmCredential: (cipherId: string) => Promise<boolean>;
pickCredential: (cipherIds: string[]) => Promise<string>;
confirmNewCredential: (params: NewCredentialParams) => Promise<boolean>;
}

View File

@ -129,7 +129,7 @@ export class Fido2Service implements Fido2ServiceAbstraction {
throw new OriginMismatchError();
}
await this.fido2UserInterfaceService.pickCredential([credential.credentialId.encoded]);
await this.fido2UserInterfaceService.confirmCredential(credential.credentialId.encoded);
} else {
// We're looking for a resident key
const credentials = await this.getCredentialsByRp(params.rpId);

View File

@ -2,6 +2,10 @@ import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } fro
import { RequestAbortedError } from "../../abstractions/fido2/fido2.service.abstraction";
export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
async confirmCredential(cipherId: string): Promise<boolean> {
return false;
}
pickCredential(cipherIds: string[]): Promise<string> {
throw new RequestAbortedError();
}