mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
Experiment: UX Poc to use passkeys for unlocking vault
This commit is contained in:
@@ -125,36 +125,26 @@
|
||||
"
|
||||
>
|
||||
<form [bitSubmit]="submit" [formGroup]="formGroup">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "masterPass" | i18n }}</bit-label>
|
||||
<input
|
||||
type="password"
|
||||
formControlName="masterPassword"
|
||||
bitInput
|
||||
appAutofocus
|
||||
name="masterPassword"
|
||||
class="tw-font-mono"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton
|
||||
bitSuffix
|
||||
bitPasswordInputToggle
|
||||
[(toggled)]="showPassword"
|
||||
></button>
|
||||
|
||||
<!-- [attr.aria-pressed]="showPassword" -->
|
||||
</bit-form-field>
|
||||
|
||||
<div class="tw-flex tw-flex-col tw-space-y-3">
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary" block>
|
||||
{{ "unlock" | i18n }}
|
||||
</button>
|
||||
<ng-container>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
bitFormButton
|
||||
buttonType="primary"
|
||||
block
|
||||
(click)="unlockViaPasskey()"
|
||||
>
|
||||
<span>Unlock with passkey</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<p class="tw-text-center">{{ "or" | i18n }}</p>
|
||||
|
||||
<button type="submit" bitButton bitFormButton buttonType="secondary" block>
|
||||
Unlock with master password
|
||||
</button>
|
||||
|
||||
<ng-container *ngIf="showBiometrics">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -351,6 +351,48 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async unlockViaPasskey(): Promise<void> {
|
||||
// get a 32 byte unlock salt from the vault
|
||||
// note: Depending on how we want to derive keys etc, this could be the same or a different salt than the one used for the sign in feature.
|
||||
const unlockKeySalt = new Uint8Array(32).fill(0); // Mocked
|
||||
|
||||
// get a list of credential ids that can be used to unlock the vault. These could be synced from the server and stored on device.
|
||||
// Knowing these will improve the UX by only showing the relevant credentials to the user.
|
||||
const credentialIdsUsedforUnlock = [
|
||||
"7ocVvTwCOoKfKaAp-FRMTPaFqB7CNOF1Z5SkAWRhIaSAko-6yyYvSnJoSzAqc6dD", // Anders yubikey
|
||||
//"g773cRzuHU2qAClY7P4hVzMhw-I", // Anders apple keychain
|
||||
];
|
||||
|
||||
const allowedCredentials = credentialIdsUsedforUnlock.map((passkey) => {
|
||||
const x = passkey.replace(/-/g, "+").replace(/_/g, "/");
|
||||
return {
|
||||
id: Uint8Array.from(atob(x), (c) => c.charCodeAt(0)),
|
||||
type: "public-key",
|
||||
};
|
||||
});
|
||||
|
||||
// Note: No input here can really be trusted to offer much security since it's generated by the client.
|
||||
const publicKey = {
|
||||
challenge: new Uint8Array(32).fill(0),
|
||||
rpId: "vault.bitwarden.com",
|
||||
allowCredentials: allowedCredentials,
|
||||
timeout: 60000,
|
||||
userVerification: "required",
|
||||
extensions: {
|
||||
prf: { eval: { first: unlockKeySalt } },
|
||||
},
|
||||
} as any;
|
||||
|
||||
const response = (await navigator.credentials.get({ publicKey })) as PublicKeyCredential;
|
||||
|
||||
// dervied key
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const prfResult = (response.getClientExtensionResults() as any).prf?.results?.first;
|
||||
|
||||
// use the prfresult in a similar way to the sign in flow.
|
||||
}
|
||||
|
||||
async unlockViaBiometrics(): Promise<void> {
|
||||
this.unlockingViaBiometrics = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user