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

[EC-598] feat: show new cipher being added

This commit is contained in:
Andreas Coroiu 2023-01-05 14:30:00 +01:00
parent 6c7548c7ec
commit f0b8d32ee6
No known key found for this signature in database
GPG Key ID: E70B5FFC81DFEC1A
7 changed files with 80 additions and 61 deletions

View File

@ -87,7 +87,7 @@ import { PrivateModeWarningComponent } from "./components/private-mode-warning.c
import { SendListComponent } from "./components/send-list.component";
import { SetPinComponent } from "./components/set-pin.component";
import { UserVerificationComponent } from "./components/user-verification.component";
import { Fido2Module } from "./fido2/fido2.module";
import { Fido2Component } from "./fido2/fido2.component";
import { GeneratorComponent } from "./generator/generator.component";
import { PasswordGeneratorHistoryComponent } from "./generator/password-generator-history.component";
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component";
@ -193,7 +193,6 @@ registerLocaleData(localeZhTw, "zh-TW");
ReactiveFormsModule,
ScrollingModule,
ServicesModule,
Fido2Module,
],
declarations: [
ActionButtonsComponent,
@ -246,6 +245,7 @@ registerLocaleData(localeZhTw, "zh-TW");
RemovePasswordComponent,
VaultSelectComponent,
AboutComponent,
Fido2Component,
],
providers: [CurrencyPipe, DatePipe],
bootstrap: [AppComponent],

View File

@ -1,16 +1,23 @@
<div class="auth-wrapper">
<ng-container *ngIf="data.type == 'VerifyUserRequest'">
A site is asking for authentication
</ng-container>
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">
A site wants to create a new passkey in your vault
</ng-container>
<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>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(true)">
Use browser built-in
</button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(false)">Abort</button>
</div>
<ng-container *ngIf="data">
<div class="auth-wrapper">
<ng-container *ngIf="data.type == 'VerifyUserRequest'">
A site is asking for authentication
</ng-container>
<ng-container *ngIf="data.type == 'ConfirmNewCredentialRequest'">
<div class="box list">
<div class="box-content">
<app-cipher-row [cipher]="cipher"></app-cipher-row>
</div>
</div>
A site wants to create a new passkey in your vault
</ng-container>
<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>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(true)">
Use browser built-in
</button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel(false)">Abort</button>
</div>
</ng-container>

View File

@ -1,5 +1,10 @@
import { Component, HostListener } from "@angular/core";
import { Component, HostListener, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { CipherType } from "@bitwarden/common/enums/cipherType";
import { CipherView } from "@bitwarden/common/models/view/cipher.view";
import { Fido2KeyView } from "@bitwarden/common/models/view/fido2-key.view";
import {
BrowserFido2Message,
@ -11,11 +16,25 @@ import {
templateUrl: "fido2.component.html",
styleUrls: [],
})
export class Fido2Component {
export class Fido2Component implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
protected data?: BrowserFido2Message;
protected cipher?: CipherView;
constructor(private activatedRoute: ActivatedRoute) {}
get data() {
return this.activatedRoute.snapshot.queryParams as BrowserFido2Message;
ngOnInit(): void {
this.activatedRoute.queryParamMap.pipe(takeUntil(this.destroy$)).subscribe((queryParamMap) => {
this.data = JSON.parse(queryParamMap.get("data"));
if (this.data?.type === "ConfirmNewCredentialRequest") {
this.cipher = new CipherView();
this.cipher.name = this.data.name;
this.cipher.type = CipherType.Fido2Key;
this.cipher.fido2Key = new Fido2KeyView();
}
});
}
async accept() {
@ -56,4 +75,9 @@ export class Fido2Component {
fallbackRequested: fallback,
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}

View File

@ -1,11 +0,0 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { Fido2Component } from "./fido2.component";
@NgModule({
imports: [CommonModule],
declarations: [Fido2Component],
exports: [Fido2Component],
})
export class Fido2Module {}

View File

@ -1,6 +1,9 @@
import { filter, first, lastValueFrom, Subject, takeUntil } from "rxjs";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/abstractions/fido2/fido2-user-interface.service.abstraction";
import {
Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction,
NewCredentialParams,
} from "@bitwarden/common/abstractions/fido2/fido2-user-interface.service.abstraction";
import { Utils } from "@bitwarden/common/misc/utils";
import { RequestAbortedError } from "../../../../../libs/common/src/abstractions/fido2/fido2.service.abstraction";
@ -18,6 +21,7 @@ export type BrowserFido2Message = { requestId: string } & (
}
| {
type: "ConfirmNewCredentialRequest";
name: string;
}
| {
type: "ConfirmNewCredentialResponse";
@ -51,7 +55,7 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
async verifyPresence(): Promise<boolean> {
const requestId = Utils.newGuid();
const data: BrowserFido2Message = { type: "VerifyUserRequest", requestId };
const queryParams = new URLSearchParams(data).toString();
const queryParams = new URLSearchParams({ data: JSON.stringify(data) }).toString();
this.popupUtilsService.popOut(
null,
`popup/index.html?uilocation=popout#/fido2?${queryParams}`,
@ -77,10 +81,10 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi
return false;
}
async confirmNewCredential(): Promise<boolean> {
async confirmNewCredential({ name }: NewCredentialParams): Promise<boolean> {
const requestId = Utils.newGuid();
const data: BrowserFido2Message = { type: "ConfirmNewCredentialRequest", requestId };
const queryParams = new URLSearchParams(data).toString();
const data: BrowserFido2Message = { type: "ConfirmNewCredentialRequest", requestId, name };
const queryParams = new URLSearchParams({ data: JSON.stringify(data) }).toString();
this.popupUtilsService.popOut(
null,
`popup/index.html?uilocation=popout#/fido2?${queryParams}`,

View File

@ -1,5 +1,9 @@
export interface NewCredentialParams {
name: string;
}
export abstract class Fido2UserInterfaceService {
verifyUser: () => Promise<boolean>;
verifyPresence: () => Promise<boolean>;
confirmNewCredential: () => Promise<boolean>;
confirmNewCredential: (params: NewCredentialParams) => Promise<boolean>;
}

View File

@ -42,7 +42,9 @@ export class Fido2Service implements Fido2ServiceAbstraction {
async createCredential(
params: CredentialRegistrationParams
): Promise<CredentialRegistrationResult> {
const presence = await this.fido2UserInterfaceService.confirmNewCredential();
const presence = await this.fido2UserInterfaceService.confirmNewCredential({
name: params.origin,
});
// eslint-disable-next-line no-console
console.log("Fido2Service.createCredential", params);
@ -123,14 +125,11 @@ export class Fido2Service implements Fido2ServiceAbstraction {
credential = await this.getCredentialByRp(params.rpId);
}
console.log("Found credential: ", credential);
if (credential === undefined) {
throw new NoCredentialFoundError();
}
if (credential.origin !== params.origin) {
console.error(`${params.origin} tried to use credential created by ${credential.origin}`);
throw new OriginMismatchError();
}
@ -203,8 +202,6 @@ export class Fido2Service implements Fido2ServiceAbstraction {
view.fido2Key.rpId = credential.rpId;
view.fido2Key.userHandle = Fido2Utils.bufferToString(credential.userHandle);
console.log("saving credential", { view, credential });
const cipher = await this.cipherService.encrypt(view);
await this.cipherService.createWithServer(cipher);
@ -237,22 +234,16 @@ interface AuthDataParams {
async function mapCipherViewToBitCredential(cipherView: CipherView): Promise<BitCredential> {
const keyBuffer = Fido2Utils.stringToBuffer(cipherView.fido2Key.key);
let privateKey;
try {
privateKey = await crypto.subtle.importKey(
"pkcs8",
keyBuffer,
{
name: "ECDSA",
namedCurve: "P-256",
},
true,
KeyUsages
);
} catch (err) {
console.log("Error importing key", { err });
throw err;
}
const privateKey = await crypto.subtle.importKey(
"pkcs8",
keyBuffer,
{
name: "ECDSA",
namedCurve: "P-256",
},
true,
KeyUsages
);
return {
credentialId: new CredentialId(cipherView.id),