mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 05:53:42 +00:00
update modal with correct ciphers and functionality
This commit is contained in:
@@ -62,9 +62,9 @@ import { SsoComponentV1 } from "../auth/sso-v1.component";
|
||||
import { TwoFactorAuthComponent } from "../auth/two-factor-auth.component";
|
||||
import { TwoFactorComponent } from "../auth/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
|
||||
import { Fido2VaultComponent } from "../modal/passkeys/fido2-vault.component";
|
||||
import { VaultComponent } from "../vault/app/vault/vault.component";
|
||||
|
||||
import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component";
|
||||
import { SendComponent } from "./tools/send/send.component";
|
||||
|
||||
/**
|
||||
@@ -334,7 +334,11 @@ const routes: Routes = [
|
||||
),
|
||||
{
|
||||
path: "passkeys",
|
||||
component: Fido2PlaceholderComponent,
|
||||
component: Fido2VaultComponent,
|
||||
},
|
||||
{
|
||||
path: "create-passkey",
|
||||
component: Fido2VaultComponent,
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
|
||||
@@ -76,9 +76,12 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
) {}
|
||||
|
||||
pickCredential: (
|
||||
async pickCredential(
|
||||
params: PickCredentialParams,
|
||||
) => Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
): Promise<{ cipherId: string; userVerified: boolean }> {
|
||||
// Add your implementation here
|
||||
return { cipherId: "", userVerified: false };
|
||||
}
|
||||
|
||||
private confirmCredentialSubject = new Subject<void>();
|
||||
private createdCipher: Cipher;
|
||||
@@ -119,7 +122,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
);
|
||||
|
||||
try {
|
||||
await this.showUi();
|
||||
await this.showUi(rpId);
|
||||
|
||||
// Wait for the UI to wrap up
|
||||
await this.waitForUiCredentialConfirmation();
|
||||
@@ -147,11 +150,14 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
}
|
||||
}
|
||||
|
||||
private async showUi() {
|
||||
private async showUi(rpId?: string) {
|
||||
// Load the UI:
|
||||
// maybe toggling to modal mode shouldn't be done here?
|
||||
await this.desktopSettingsService.setInModalMode(true);
|
||||
await this.router.navigate(["/passkeys"]);
|
||||
//pass the rpid to the fido2placeholder component through routing parameter
|
||||
|
||||
// await this.router.navigate(["/passkeys"]);
|
||||
await this.router.navigate(["/passkeys"], { state: { rpid: rpId } });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3430,5 +3430,8 @@
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
},
|
||||
"passkeyLogin":{
|
||||
"message": "Log in with passkey?"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,14 @@ export class TrayMain {
|
||||
},
|
||||
{
|
||||
visible: isDev(),
|
||||
label: "Fake Popup",
|
||||
label: "Fake Popup Select",
|
||||
click: () => this.fakePopup(),
|
||||
},
|
||||
{
|
||||
visible: isDev(),
|
||||
label: "Fake Popup Create",
|
||||
click: () => this.fakePopupCreate(),
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: this.i18nService.t("exit"),
|
||||
@@ -212,4 +217,8 @@ export class TrayMain {
|
||||
private async fakePopup() {
|
||||
await this.messagingService.send("loadurl", { url: "/passkeys", modal: true });
|
||||
}
|
||||
|
||||
private async fakePopupCreate() {
|
||||
await this.messagingService.send("loadurl", { url: "/passkeys", modal: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<bit-section
|
||||
disableMargin
|
||||
class="tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
|
||||
>
|
||||
<bit-section-header class="tw-bg-background">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<bit-icon [icon]="Icons.BitwardenShield" class="tw-w-10 tw-mt-2 tw-ml-2"></bit-icon>
|
||||
|
||||
<h2 bitTypography="h6">{{ "passkeyLogin" | i18n }}Log in with passkey?</h2>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-close"
|
||||
slot="end"
|
||||
class="tw-mb-4 tw-mr-2"
|
||||
(click)="closeModal()"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</bit-section-header>
|
||||
</bit-section>
|
||||
|
||||
<bit-section class="tw-bg-background-alt tw-p-4">
|
||||
<bit-item *ngFor="let c of ciphers" class="">
|
||||
<button type="button" bit-item-content (click)="confirmPasskey()">
|
||||
<app-vault-icon [cipher]="c" slot="start"></app-vault-icon>
|
||||
<button bitLink [title]="c.name" type="button">
|
||||
{{ c.name }}
|
||||
</button>
|
||||
<span slot="secondary">{{ c.subTitle }}</span>
|
||||
<span bitBadge slot="end">Select</span>
|
||||
</button>
|
||||
</bit-item>
|
||||
</bit-section>
|
||||
</div>
|
||||
102
apps/desktop/src/modal/passkeys/create/fido2-vault.component.ts
Normal file
102
apps/desktop/src/modal/passkeys/create/fido2-vault.component.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { RouterModule, Router } from "@angular/router";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import {
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
IconModule,
|
||||
ItemModule,
|
||||
SectionComponent,
|
||||
TableModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { BitwardenShield } from "../../../../../../libs/auth/src/angular/icons";
|
||||
import { BitIconButtonComponent } from "../../../../../../libs/components/src/icon-button/icon-button.component";
|
||||
import { SectionHeaderComponent } from "../../../../../../libs/components/src/section/section-header.component";
|
||||
import {
|
||||
DesktopFido2UserInterfaceService,
|
||||
DesktopFido2UserInterfaceSession,
|
||||
} from "../../../autofill/services/desktop-fido2-user-interface.service";
|
||||
import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service";
|
||||
// import { AnchorLinkDirective } from "../../../../../libs/components/src/link/link.directive";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
SectionHeaderComponent,
|
||||
BitIconButtonComponent,
|
||||
TableModule,
|
||||
JslibModule,
|
||||
IconModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
SectionComponent,
|
||||
ItemModule,
|
||||
BadgeModule,
|
||||
],
|
||||
templateUrl: "fido2-vault.component.html",
|
||||
})
|
||||
export class Fido2VaultComponent implements OnInit {
|
||||
ciphers: CipherView[];
|
||||
rpId: string;
|
||||
readonly Icons = { BitwardenShield };
|
||||
|
||||
session?: DesktopFido2UserInterfaceSession = null;
|
||||
constructor(
|
||||
private readonly desktopSettingsService: DesktopSettingsService,
|
||||
private readonly fido2UserInterfaceService: DesktopFido2UserInterfaceService,
|
||||
private readonly cipherService: CipherService,
|
||||
private readonly router: Router,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.rpId = history.state.rpid;
|
||||
this.session = this.fido2UserInterfaceService.getCurrentSession();
|
||||
if (this.rpId !== null) {
|
||||
this.ciphers = await this.cipherService.getPasskeyCiphersForUrl(this.rpId);
|
||||
} else {
|
||||
this.ciphers = await this.cipherService.getAllDecrypted();
|
||||
}
|
||||
}
|
||||
|
||||
async confirmPasskey() {
|
||||
try {
|
||||
// Retrieve the current UI session to control the flow
|
||||
if (!this.session) {
|
||||
// todo: handle error
|
||||
throw new Error("No session found");
|
||||
}
|
||||
|
||||
// If we want to we could submit information to the session in order to create the credential
|
||||
// const cipher = await session.createCredential({
|
||||
// userHandle: "userHandle2",
|
||||
// userName: "username2",
|
||||
// credentialName: "zxsd2",
|
||||
// rpId: "webauthn.io",
|
||||
// userVerification: true,
|
||||
// });
|
||||
|
||||
this.session.notifyConfirmCredential();
|
||||
|
||||
// Not sure this clean up should happen here or in session.
|
||||
// The session currently toggles modal on and send us here
|
||||
// But if this route is somehow opened outside of session we want to make sure we clean up?
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
} catch (error) {
|
||||
// TODO: Handle error appropriately
|
||||
}
|
||||
}
|
||||
|
||||
async closeModal() {
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
}
|
||||
}
|
||||
36
apps/desktop/src/modal/passkeys/fido2-vault.component.html
Normal file
36
apps/desktop/src/modal/passkeys/fido2-vault.component.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<bit-section
|
||||
disableMargin
|
||||
class="tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
|
||||
>
|
||||
<bit-section-header class="tw-bg-background">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<bit-icon [icon]="Icons.BitwardenShield" class="tw-w-10 tw-mt-2 tw-ml-2"></bit-icon>
|
||||
|
||||
<h2 bitTypography="h6">{{ "passkeyLogin" | i18n }}Log in with passkey?</h2>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-close"
|
||||
slot="end"
|
||||
class="tw-mb-4 tw-mr-2"
|
||||
(click)="closeModal()"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</bit-section-header>
|
||||
</bit-section>
|
||||
|
||||
<bit-section class="tw-bg-background-alt tw-p-4">
|
||||
<bit-item *ngFor="let c of ciphers" class="">
|
||||
<button type="button" bit-item-content (click)="confirmPasskey()">
|
||||
<app-vault-icon [cipher]="c" slot="start"></app-vault-icon>
|
||||
<button bitLink [title]="c.name" type="button">
|
||||
{{ c.name }}
|
||||
</button>
|
||||
<span slot="secondary">{{ c.subTitle }}</span>
|
||||
<span bitBadge slot="end">Select</span>
|
||||
</button>
|
||||
</bit-item>
|
||||
</bit-section>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { RouterModule , Router } from "@angular/router";
|
||||
import { RouterModule, Router } from "@angular/router";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -25,8 +25,6 @@ import {
|
||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||
// import { AnchorLinkDirective } from "../../../../../libs/components/src/link/link.directive";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
@@ -43,62 +41,11 @@ import { DesktopSettingsService } from "../../platform/services/desktop-settings
|
||||
ItemModule,
|
||||
BadgeModule,
|
||||
],
|
||||
template: `
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<bit-section-header class="tw-p-4 tw-items-center">
|
||||
<bit-icon [icon]="Icons.BitwardenShield" class=" tw-w-10"></bit-icon>
|
||||
|
||||
<h2 bitTypography="h6">Log in with passkey?</h2>
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-close"
|
||||
slot="end"
|
||||
class="tw-align-center"
|
||||
(click)="closeModal()"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</bit-section-header>
|
||||
<!-- insert wrapper and foreach for ciphers -->
|
||||
<!-- <bit-table> -->
|
||||
<bit-section class="tw-bg-background-alt tw-p-4">
|
||||
<ng-container *ngFor="let c of ciphers" class="tw-p-4">
|
||||
<bit-item class=" tw-mb-2 tw-py-2 tw-px-4">
|
||||
<div class="tw-flex tw-items-center tw-justify-between tw-w-full">
|
||||
<div class="tw-flex">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
|
||||
<div class="">
|
||||
<button
|
||||
bitLink
|
||||
class="tw-overflow-hidden tw-text-start tw-leading-snug"
|
||||
queryParamsHandling="merge"
|
||||
[title]="c.name"
|
||||
type="button"
|
||||
appStopProp
|
||||
aria-haspopup="true"
|
||||
>
|
||||
{{ c.name }}
|
||||
</button>
|
||||
<br />
|
||||
<span class="tw-text-sm tw-text-muted" appStopProp>{{ c.subTitle }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span bitBadge (click)="confirmPasskey()">Select</span>
|
||||
</div>
|
||||
</bit-item>
|
||||
</ng-container>
|
||||
</bit-section>
|
||||
<!-- </bit-table> -->
|
||||
<br />
|
||||
<button style="" bitButton type="button" buttonType="secondary" [loading]="false">
|
||||
Confirm passkey
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
templateUrl: "fido2-vault.component.html",
|
||||
})
|
||||
export class Fido2PlaceholderComponent implements OnInit {
|
||||
export class Fido2VaultComponent implements OnInit {
|
||||
ciphers: CipherView[];
|
||||
rpId: string;
|
||||
readonly Icons = { BitwardenShield };
|
||||
|
||||
session?: DesktopFido2UserInterfaceSession = null;
|
||||
@@ -110,8 +57,13 @@ export class Fido2PlaceholderComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.rpId = history.state.rpid;
|
||||
this.session = this.fido2UserInterfaceService.getCurrentSession();
|
||||
this.ciphers = await this.cipherService.getAllDecrypted();
|
||||
if (this.rpId !== null) {
|
||||
this.ciphers = await this.cipherService.getPasskeyCiphersForUrl(this.rpId);
|
||||
} else {
|
||||
this.ciphers = await this.cipherService.getAllDecrypted();
|
||||
}
|
||||
}
|
||||
|
||||
async confirmPasskey() {
|
||||
@@ -45,6 +45,7 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
includeOtherTypes?: CipherType[],
|
||||
defaultMatch?: UriMatchStrategySetting,
|
||||
) => Promise<CipherView[]>;
|
||||
getPasskeyCiphersForUrl: (url: string) => Promise<CipherView[]>;
|
||||
filterCiphersForUrl: (
|
||||
ciphers: CipherView[],
|
||||
url: string,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
shareReplay,
|
||||
Subject,
|
||||
switchMap,
|
||||
tap,
|
||||
//tap,
|
||||
} from "rxjs";
|
||||
import { SemVer } from "semver";
|
||||
|
||||
@@ -142,7 +142,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
this.cipherViews$ = combineLatest([this.encryptedCiphersState.state$, this.localData$]).pipe(
|
||||
filter(([ciphers]) => ciphers != null), // Skip if ciphers haven't been loaded yor synced yet
|
||||
switchMap(() => merge(this.forceCipherViews$, this.getAllDecrypted())),
|
||||
tap((v) => console.log("---- cipherViews$", v)),
|
||||
// tap((v) => console.log("---- cipherViews$", v)),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
);
|
||||
this.addEditCipherInfo$ = this.addEditCipherInfoState.state$;
|
||||
@@ -1632,6 +1632,15 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
async getPasskeyCiphersForUrl(url: string): Promise<CipherView[]> {
|
||||
let ciphers = await this.getAllDecryptedForUrl(url);
|
||||
if (!ciphers) {
|
||||
return null;
|
||||
}
|
||||
ciphers = ciphers.filter((cipher) => cipher.login.fido2Credentials?.length);
|
||||
return ciphers;
|
||||
}
|
||||
|
||||
private async clearEncryptedCiphersState(userId: UserId) {
|
||||
await this.stateProvider.setUserState(ENCRYPTED_CIPHERS, {}, userId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user