1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-02-15 01:11:47 +01:00

PM-8113 - TwoFactorAuthComponent - pull webauthn fallback response handling into primary init with checks based on client for if it should be processed.

This commit is contained in:
Jared Snider 2024-12-14 16:20:12 -05:00
parent 831328c83c
commit 42719e4f13
No known key found for this signature in database
GPG Key ID: A149DDD612516286
4 changed files with 39 additions and 24 deletions

View File

@ -10,6 +10,10 @@ export class ExtensionTwoFactorAuthComponentService
extends DefaultTwoFactorAuthComponentService
implements TwoFactorAuthComponentService
{
shouldCheckForWebauthnResponseOnInit(): boolean {
return true;
}
async handleSso2faFlowSuccess(): Promise<void> {
// Force sidebars (FF && Opera) to reload while exempting current window
// because we are just going to close the current window.

View File

@ -4,6 +4,10 @@ import {
} from "./two-factor-auth-component.service";
export class DefaultTwoFactorAuthComponentService implements TwoFactorAuthComponentService {
shouldCheckForWebauthnResponseOnInit() {
return false;
}
determineLegacyKeyMigrationAction() {
return LegacyKeyMigrationAction.PREVENT_LOGIN_AND_SHOW_REQUIRE_MIGRATION_WARNING;
}

View File

@ -8,6 +8,11 @@ export enum LegacyKeyMigrationAction {
* implementation for all clients.
*/
export abstract class TwoFactorAuthComponentService {
/**
* Determines if the client should check for a webauthn response on init.
* Currently, only the extension should check on init.
*/
abstract shouldCheckForWebauthnResponseOnInit(): boolean;
/**
* We used to use the user's master key to encrypt their data. We deprecated that approach
* and now use a user key. This method should be called if we detect that the user

View File

@ -147,34 +147,44 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
async ngOnInit() {
this.inSsoFlow = this.activatedRoute.snapshot.queryParamMap.get("sso") === "true";
this.orgSsoIdentifier = this.activatedRoute.snapshot.queryParamMap.get("identifier");
this.listenFor2faSessionTimeout();
await this.determine2faProvider();
await this.setTitleByTwoFactorProvider();
if (this.twoFactorAuthComponentService.shouldCheckForWebauthnResponseOnInit()) {
await this.processWebAuthnResponseIfExists();
}
await this.setSelected2faProviderType();
await this.set2faProviderData();
await this.setTitleByTwoFactorProviderType();
this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
this.token = value.token;
this.remember = value.remember;
});
this.listenFor2faSessionTimeout();
// TODO: this is a temporary on init. Must genericize this and refactor out client specific stuff where possible.
if (this.clientType === ClientType.Browser) {
await this.extensionOnInit();
}
}
private async determine2faProvider() {
private async processWebAuthnResponseIfExists() {
const webAuthn2faResponse = this.activatedRoute.snapshot.queryParamMap.get("webAuthnResponse");
if (webAuthn2faResponse) {
this.selectedProviderType = TwoFactorProviderType.WebAuthn;
await this.set2faProviderData();
this.token = webAuthn2faResponse;
this.remember = this.activatedRoute.snapshot.queryParamMap.get("remember") === "true";
} else {
const webAuthnSupported = this.platformUtilsService.supportsWebAuthn(this.win);
this.selectedProviderType = await this.twoFactorService.getDefaultProvider(webAuthnSupported);
await this.submit();
}
}
private async setSelected2faProviderType() {
const webAuthnSupported = this.platformUtilsService.supportsWebAuthn(this.win);
this.selectedProviderType = await this.twoFactorService.getDefaultProvider(webAuthnSupported);
}
private async set2faProviderData() {
const providerData = await this.twoFactorService.getProviders().then((providers) => {
return providers.get(this.selectedProviderType);
});
@ -182,20 +192,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
}
private async extensionOnInit() {
if (this.activatedRoute.snapshot.paramMap.has("webAuthnResponse")) {
// WebAuthn fallback response
this.selectedProviderType = TwoFactorProviderType.WebAuthn;
this.token = this.activatedRoute.snapshot.paramMap.get("webAuthnResponse");
// TODO: move this to service.
// this.onSuccessfulLogin = async () => {
// this.messagingService.send("reloadPopup");
// window.close();
// };
this.remember = this.activatedRoute.snapshot.paramMap.get("remember") === "true";
await this.submit();
return;
}
// WebAuthn prompt appears inside the popup on linux, and requires a larger popup width
// than usual to avoid cutting off the dialog.
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && (await this.isLinux())) {
@ -275,7 +271,7 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
});
this.providerData = providerData;
this.selectedProviderType = response.type;
await this.setTitleByTwoFactorProvider();
await this.setTitleByTwoFactorProviderType();
}
}
@ -308,7 +304,7 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
return true;
}
async setTitleByTwoFactorProvider() {
async setTitleByTwoFactorProviderType() {
if (this.selectedProviderType == null) {
this.title = this.i18nService.t("loginUnavailable");
return;
@ -368,6 +364,12 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
return;
}
// TODO: test extension + webauthn 2fa to ensure these calls are no longer necessary:
// this.onSuccessfulLogin = async () => {
// this.messagingService.send("reloadPopup"); // this is just a navigate to "/"
// window.close();
// };
const defaultSuccessRoute = await this.determineDefaultSuccessRoute();
await this.router.navigate([defaultSuccessRoute], {