diff --git a/apps/browser/src/auth/services/extension-two-factor-auth-component.service.ts b/apps/browser/src/auth/services/extension-two-factor-auth-component.service.ts index 67492e97e3..03d76dd47f 100644 --- a/apps/browser/src/auth/services/extension-two-factor-auth-component.service.ts +++ b/apps/browser/src/auth/services/extension-two-factor-auth-component.service.ts @@ -20,7 +20,7 @@ export class ExtensionTwoFactorAuthComponentService super(); } - shouldCheckForWebauthnResponseOnInit(): boolean { + shouldCheckForWebAuthnQueryParamResponse(): boolean { return true; } diff --git a/apps/browser/src/auth/services/extension-two-factor-auth-webauthn-component.service.ts b/apps/browser/src/auth/services/extension-two-factor-auth-webauthn-component.service.ts index 14a949fb08..84a54cbc12 100644 --- a/apps/browser/src/auth/services/extension-two-factor-auth-webauthn-component.service.ts +++ b/apps/browser/src/auth/services/extension-two-factor-auth-webauthn-component.service.ts @@ -12,6 +12,12 @@ export class ExtensionTwoFactorAuthWebAuthnComponentService super(); } + /** + * In the browser extension, we open webAuthn in a new web client tab sometimes due to inline + * WebAuthn Iframe's not working in some browsers. We open a 2FA popout upon successful + * completion of WebAuthn submission with query parameters to finish the 2FA process. + * @returns boolean + */ shouldOpenWebAuthnInNewTab(): boolean { const isChrome = this.platformUtilsService.isChrome(); if (isChrome) { diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn-component.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn-component.service.ts index e448d97cc3..49842c5379 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn-component.service.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn-component.service.ts @@ -4,6 +4,7 @@ export abstract class TwoFactorAuthWebAuthnComponentService { /** * Determines if the WebAuthn 2FA should be opened in a new tab or can be completed in the current tab. + * In a browser extension context, we open WebAuthn in a new web client tab. */ abstract shouldOpenWebAuthnInNewTab(): boolean; } diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts index aefdc10df2..61ce984a01 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/two-factor-auth-webauthn.component.ts @@ -25,6 +25,11 @@ import { import { TwoFactorAuthWebAuthnComponentService } from "./two-factor-auth-webauthn-component.service"; +export interface WebAuthnResult { + token: string; + remember?: boolean; +} + @Component({ standalone: true, selector: "app-two-factor-auth-webauthn", @@ -44,7 +49,7 @@ import { TwoFactorAuthWebAuthnComponentService } from "./two-factor-auth-webauth providers: [], }) export class TwoFactorAuthWebAuthnComponent implements OnInit, OnDestroy { - @Output() token = new EventEmitter(); + @Output() webAuthnResultEmitter = new EventEmitter(); webAuthnReady = false; webAuthnNewTab = false; @@ -67,17 +72,26 @@ export class TwoFactorAuthWebAuthnComponent implements OnInit, OnDestroy { } async ngOnInit(): Promise { - if (this.route.snapshot.paramMap.has("webAuthnResponse")) { - const webAuthnResponse = this.route.snapshot.paramMap.get("webAuthnResponse"); - - if (webAuthnResponse != null) { - // TODO: determine if we even need this with the top level processing of the webauthn response. - this.token.emit(webAuthnResponse); - // TODO: should we early return? - // return; - } + if (this.webAuthnNewTab && this.route.snapshot.paramMap.has("webAuthnResponse")) { + this.submitWebAuthnNewTabResponse(); + } else { + await this.buildWebAuthnIFrame(); } + } + private submitWebAuthnNewTabResponse() { + const webAuthnNewTabResponse = this.route.snapshot.paramMap.get("webAuthnResponse"); + const remember = this.route.snapshot.queryParamMap.get("remember") === "true"; + + if (webAuthnNewTabResponse != null) { + this.webAuthnResultEmitter.emit({ + token: webAuthnNewTabResponse, + remember, + }); + } + } + + private async buildWebAuthnIFrame() { if (this.win != null && this.webAuthnSupported) { const env = await firstValueFrom(this.environmentService.environment$); const webVaultUrl = env.getWebVaultUrl(); @@ -88,7 +102,7 @@ export class TwoFactorAuthWebAuthnComponent implements OnInit, OnDestroy { this.platformUtilsService, this.i18nService, (token: string) => { - this.token.emit(token); + this.webAuthnResultEmitter.emit({ token }); }, (error: string) => { this.toastService.showToast({ diff --git a/libs/auth/src/angular/two-factor-auth/default-two-factor-auth-component.service.ts b/libs/auth/src/angular/two-factor-auth/default-two-factor-auth-component.service.ts index 579a71aa4b..67a74158be 100644 --- a/libs/auth/src/angular/two-factor-auth/default-two-factor-auth-component.service.ts +++ b/libs/auth/src/angular/two-factor-auth/default-two-factor-auth-component.service.ts @@ -4,7 +4,7 @@ import { } from "./two-factor-auth-component.service"; export class DefaultTwoFactorAuthComponentService implements TwoFactorAuthComponentService { - shouldCheckForWebauthnResponseOnInit() { + shouldCheckForWebAuthnQueryParamResponse() { return false; } diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts index 487f9be9ac..2e71f637ea 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth-component.service.ts @@ -12,9 +12,9 @@ export enum LegacyKeyMigrationAction { export abstract class TwoFactorAuthComponentService { /** * Determines if the client should check for a webauthn response on init. - * Currently, only the extension should check on init. + * Currently, only the extension should check during component initialization. */ - abstract shouldCheckForWebauthnResponseOnInit(): boolean; + abstract shouldCheckForWebAuthnQueryParamResponse(): boolean; /** * Extends the popup width if required. diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.html b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.html index b1f041e795..e9717f7a0e 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.html +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.html @@ -19,7 +19,7 @@ *ngIf="selectedProviderType === providerType.Yubikey" />