1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-02-19 01:51:27 +01:00

PM-8113 - Webauthn 2FA - As webauthn popout doesn't persist SSO state, have to genercize success logic (which should be a good thing but requires confirmation testing).

This commit is contained in:
Jared Snider 2025-01-20 12:32:13 -05:00
parent 2b9f97a7b6
commit 5b6f5520d1
No known key found for this signature in database
GPG Key ID: A149DDD612516286
4 changed files with 49 additions and 19 deletions

View File

@ -5,7 +5,12 @@ import {
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
import { closeSsoAuthResultPopout } from "../popup/utils/auth-popout-window"; import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
import {
AuthPopoutType,
closeSsoAuthResultPopout,
closeTwoFactorAuthPopout,
} from "../popup/utils/auth-popout-window";
export class ExtensionTwoFactorAuthComponentService export class ExtensionTwoFactorAuthComponentService
extends DefaultTwoFactorAuthComponentService extends DefaultTwoFactorAuthComponentService
@ -36,17 +41,40 @@ export class ExtensionTwoFactorAuthComponentService
this.window.close(); this.window.close();
} }
async handleSso2faFlowSuccess(): Promise<void> { async handle2faSuccess(): Promise<void> {
// TODO: confirm that moving this from SSO flow only to general flow doesn't introduce any issues
// Force sidebars (FF && Opera) to reload while exempting current window // Force sidebars (FF && Opera) to reload while exempting current window
// because we are just going to close the current window. // because we are just going to close the current window if it is in a popout
// or navigate forward if it is in the popup
BrowserApi.reloadOpenWindows(true); BrowserApi.reloadOpenWindows(true);
// We don't need this window anymore because the intent is for the user to be left await this.closeSingleActionPopouts();
// on the web vault screen which tells them to continue in the browser extension (sidebar or popup) }
// We don't want the user to be left with a floating, popped out extension which could be lost behind
// another window or minimized. Currently, the popped out window thinks it is active and wouldn't time out private async closeSingleActionPopouts(): Promise<void> {
// which leads to the security concern. So, we close the popped out extension to avoid this. // If we are in a single action popout, we don't need the popout anymore because the intent
await closeSsoAuthResultPopout(); // is for the user to be left on the web vault screen which tells them to continue in
// the browser extension (sidebar or popup). We don't want the user to be left with a
// floating, popped out extension which could be lost behind another window or minimized.
// Currently, the popped out window thinks it is active and wouldn't time out which
// leads to the security concern. So, we close the popped out extension to avoid this.
const inSsoAuthResultPopout = BrowserPopupUtils.inSingleActionPopout(
this.window,
AuthPopoutType.ssoAuthResult,
);
if (inSsoAuthResultPopout) {
await closeSsoAuthResultPopout();
return;
}
const inTwoFactorAuthPopout = BrowserPopupUtils.inSingleActionPopout(
this.window,
AuthPopoutType.twoFactorAuth,
);
if (inTwoFactorAuthPopout) {
await closeTwoFactorAuthPopout();
}
} }
private async isLinux(): Promise<boolean> { private async isLinux(): Promise<boolean> {

View File

@ -72,7 +72,10 @@ export class TwoFactorAuthWebAuthnComponent implements OnInit, OnDestroy {
const webAuthnResponse = this.route.snapshot.paramMap.get("webAuthnResponse"); const webAuthnResponse = this.route.snapshot.paramMap.get("webAuthnResponse");
if (webAuthnResponse != null) { if (webAuthnResponse != null) {
// TODO: determine if we even need this with the top level processing of the webauthn response.
this.token.emit(webAuthnResponse); this.token.emit(webAuthnResponse);
// TODO: should we early return?
// return;
} }
} }

View File

@ -47,8 +47,8 @@ export abstract class TwoFactorAuthComponentService {
abstract determineLegacyKeyMigrationAction(): LegacyKeyMigrationAction; abstract determineLegacyKeyMigrationAction(): LegacyKeyMigrationAction;
/** /**
* Optionally handles the success flow for the SSO + 2FA required flow. * Optionally executes 2FA success logic.
* Only defined on clients that require custom success handling. * Only defined on clients that require custom success handling.
*/ */
abstract handleSso2faFlowSuccess?(): Promise<void>; abstract handle2faSuccess?(): Promise<void>;
} }

View File

@ -146,6 +146,8 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
if (this.twoFactorAuthComponentService.shouldCheckForWebauthnResponseOnInit()) { if (this.twoFactorAuthComponentService.shouldCheckForWebauthnResponseOnInit()) {
await this.processWebAuthnResponseIfExists(); await this.processWebAuthnResponseIfExists();
// TODO: should we return here?
// return;
} }
await this.setSelected2faProviderType(); await this.setSelected2faProviderType();
@ -336,12 +338,9 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
return await this.handleChangePasswordRequired(this.orgSsoIdentifier); return await this.handleChangePasswordRequired(this.orgSsoIdentifier);
} }
// if we are in the SSO flow and we have a custom success handler, call it // if we have a custom success handler, call it
if ( if (this.twoFactorAuthComponentService.handle2faSuccess !== undefined) {
this.inSsoFlow && await this.twoFactorAuthComponentService.handle2faSuccess();
this.twoFactorAuthComponentService.handleSso2faFlowSuccess !== undefined
) {
await this.twoFactorAuthComponentService.handleSso2faFlowSuccess();
return; return;
} }
@ -395,8 +394,8 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
); );
} }
if (this.twoFactorAuthComponentService.handleSso2faFlowSuccess !== undefined) { if (this.twoFactorAuthComponentService.handle2faSuccess !== undefined) {
await this.twoFactorAuthComponentService.handleSso2faFlowSuccess(); await this.twoFactorAuthComponentService.handle2faSuccess();
return; return;
} }