mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-03 18:28:13 +01:00
Auth/PM-5368 & PM-4613 - Web & Browser - Add support for new 2FA Duo Frameless Redirect flow (#7670)
* [PM-5368] Open Duo auth url. Add BroadcastChannel listener for duo result. * [PM-5368] Remove debug line. Use PlatformUtilService to launch Uri. * PM-5368 - Some progress on getting new frameless duo implementation in place * PM-5368 - Base2FAComp - Save off duoFramelessUrl for use later on as user must be given the option to remember the device before launching the duo frameless flow in the new tab. * PM-5368 - Web - 2FA Comp - (1) Only show larger width when showing backwards compatible duo (2) Stack buttons per new design (3) selectedProviderType === providerType.OrganizationDuo is correct check for when org requires DUO * PM-5368 - Web - 2FA Comp - translate duo stuff * PM-4613 - Browser 2FA - Get most of DUO frameless in place. WIP. Must figure out how to transfer state from popup to popout + add popout logic to auth-popout-windows.ts. Converted existing useAnotherTwoStepMethod button to use new comp lib bitButton per design. * PM-4613 - Browser 2FA Comp - (1) HTML - add margin around duo frameless text to match figma (2) Get popout extension logic working properly - now closes existing popup * PM-4613 - TODO figure out communication between web and browser as broadcast channel will not work. * PM-5368 - Base comp + web changes - (1) Base component now has a setupDuoResultListener method for child classes to override (2) Web overrides setupDuoResultListener and cleans up broadcast channel once a duo result comes through. * PM-4613 - Browser - (1) Add window message handling to content-message-handler content script to pass along the duo result message to the browser extension (2) 2FA comp - override setupDuoResultListener and use browserMessagingApi to listen to duoResult and submit when it comes through. * PM-5368 - Web - 2FA comp - only clean up duo result channel on ngDestroy so that user can re-submit if an error occurs. * PM-5368 and PM-4613 - (1) Update base 2FA comp to only initialize duo result listener once as init is called any time the user changes 2FA option if multiple are present (duo org and duo personal) (2) Each client now will only create a listener once even if it is called more than once (3) On web, only try to clean up the duoResultChannel if it was created to avoid erroring on other 2FA methods. * PM-5368 - Base 2FA comp - add TODO to remove duo SDK handling once we remove the duo-redirect flag * PM-5368 - Per PR feedback, avoid repetition of duo provider check by using a new public property for isDuoProvider * PM-4613 - Per PR feedback: (1) Deconstruct code out of data (2) Add test for duoResult. --------- Co-authored-by: André Bispo <abispo@bitwarden.com>
This commit is contained in:
parent
414ee2563f
commit
c91ceb2014
@ -2695,6 +2695,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"launchDuoAndFollowStepsToFinishLoggingIn": {
|
||||
"message": "Launch DUO and follow the steps to finish logging in."
|
||||
},
|
||||
"duoRequiredByOrgForAccount": {
|
||||
"message": "DUO two-step login is required for your account."
|
||||
},
|
||||
"openExtensionInNewWindowToCompleteLogin": {
|
||||
"message": "Open the extension in a new window to complete login"
|
||||
},
|
||||
"popoutExtension": {
|
||||
"message": "Popout extension"
|
||||
},
|
||||
"launchDuo": {
|
||||
"message": "Launch DUO"
|
||||
},
|
||||
"importFormatError": {
|
||||
"message": "Data is not formatted correctly. Please check your import file and try again."
|
||||
},
|
||||
|
@ -12,8 +12,7 @@
|
||||
[disabled]="form.loading"
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo &&
|
||||
!isDuoProvider &&
|
||||
(selectedProviderType !== providerType.WebAuthn || form.loading)
|
||||
"
|
||||
>
|
||||
@ -23,6 +22,7 @@
|
||||
</div>
|
||||
</header>
|
||||
<main tabindex="-1">
|
||||
<!-- Authenticator / Email -->
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Authenticator ||
|
||||
@ -59,6 +59,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<!-- YubiKey -->
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<div class="content text-center">
|
||||
<p class="text-center">{{ "insertYubiKey" | i18n }}</p>
|
||||
@ -85,6 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<!-- WebAuthN (not-webAuthN tab) -->
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && !webAuthnNewTab">
|
||||
<div id="web-authn-frame">
|
||||
<iframe id="webauthn_iframe" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||
@ -98,6 +100,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<!-- WebAuthN (webAuthN tab) -->
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && webAuthnNewTab">
|
||||
<div class="content text-center" *ngIf="webAuthnNewTab">
|
||||
<p class="text-center">{{ "webAuthnNewTab" | i18n }}</p>
|
||||
@ -106,18 +109,39 @@
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
"
|
||||
<!-- Duo -->
|
||||
<ng-container *ngIf="isDuoProvider">
|
||||
<div *ngIf="duoFrameless" class="tw-my-4">
|
||||
<p
|
||||
*ngIf="selectedProviderType === providerType.OrganizationDuo"
|
||||
class="tw-mb-0 tw-text-center"
|
||||
>
|
||||
{{ "duoRequiredByOrgForAccount" | i18n }}
|
||||
</p>
|
||||
|
||||
<p class="tw-text-center" *ngIf="!inPopout">
|
||||
{{ "openExtensionInNewWindowToCompleteLogin" | i18n }}
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="inPopout">
|
||||
<p class="tw-text-center">{{ "launchDuoAndFollowStepsToFinishLoggingIn" | i18n }}</p>
|
||||
|
||||
<ng-container *ngTemplateOutlet="duoRememberMe"></ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!duoFrameless">
|
||||
<div id="duo-frame">
|
||||
<iframe
|
||||
id="duo_iframe"
|
||||
sandbox="allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<ng-container *ngTemplateOutlet="duoRememberMe"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #duoRememberMe>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
@ -126,6 +150,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||
@ -134,12 +159,47 @@
|
||||
<p class="text-center">{{ "noTwoStepProviders" | i18n }}</p>
|
||||
<p class="text-center">{{ "noTwoStepProviders2" | i18n }}</p>
|
||||
</div>
|
||||
<!-- Buttons -->
|
||||
<div class="content no-vpad" *ngIf="selectedProviderType != null">
|
||||
<p class="text-center">
|
||||
<button type="button" appStopClick (click)="anotherMethod()">
|
||||
<ng-container *ngIf="duoFrameless && isDuoProvider">
|
||||
<button
|
||||
*ngIf="inPopout"
|
||||
bitButton
|
||||
type="button"
|
||||
class="tw-mb-2"
|
||||
buttonType="primary"
|
||||
[block]="true"
|
||||
appStopClick
|
||||
(click)="launchDuoFrameless()"
|
||||
>
|
||||
{{ "launchDuo" | i18n }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="!inPopout"
|
||||
bitButton
|
||||
type="button"
|
||||
class="tw-mb-2"
|
||||
buttonType="primary"
|
||||
[block]="true"
|
||||
appStopClick
|
||||
(click)="popoutCurrentPage()"
|
||||
>
|
||||
{{ "popoutExtension" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<button
|
||||
bitButton
|
||||
type="button"
|
||||
buttonType="secondary"
|
||||
[block]="true"
|
||||
appStopClick
|
||||
(click)="anotherMethod()"
|
||||
>
|
||||
{{ "useAnotherTwoStepMethod" | i18n }}
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<p *ngIf="selectedProviderType === providerType.Email" class="text-center">
|
||||
<button type="button" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, Inject } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { first } from "rxjs/operators";
|
||||
import { Subject, Subscription } from "rxjs";
|
||||
import { filter, first, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component";
|
||||
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||
@ -22,6 +23,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service";
|
||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
||||
|
||||
import { closeTwoFactorAuthPopout } from "./utils/auth-popout-window";
|
||||
@ -33,6 +35,9 @@ const BroadcasterSubscriptionId = "TwoFactorComponent";
|
||||
templateUrl: "two-factor.component.html",
|
||||
})
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
private destroy$ = new Subject<void>();
|
||||
inPopout = BrowserPopupUtils.inPopout(window);
|
||||
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
@ -52,6 +57,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
configService: ConfigServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
@Inject(WINDOW) protected win: Window,
|
||||
private browserMessagingApi: ZonedMessageListenerService,
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
@ -158,6 +164,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
|
||||
if (this.selectedProviderType === TwoFactorProviderType.WebAuthn && (await this.isLinux())) {
|
||||
@ -182,7 +191,30 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async popoutCurrentPage() {
|
||||
await BrowserPopupUtils.openCurrentPagePopout(window);
|
||||
}
|
||||
|
||||
async isLinux() {
|
||||
return (await BrowserApi.getPlatformInfo()).os === "linux";
|
||||
}
|
||||
|
||||
duoResultSubscription: Subscription;
|
||||
|
||||
protected override setupDuoResultListener() {
|
||||
if (!this.duoResultSubscription) {
|
||||
this.duoResultSubscription = this.browserMessagingApi
|
||||
.messageListener$()
|
||||
.pipe(
|
||||
filter((msg: any) => msg.command === "duoResult"),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((msg: { command: string; code: string }) => {
|
||||
this.token = msg.code;
|
||||
// This floating promise is intentional. We don't need to await the submit + awaiting in a subscription is not recommended.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.submit();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ type ContentMessageWindowEventHandlers = {
|
||||
[key: string]: ({ data, referrer }: ContentMessageWindowEventParams) => void;
|
||||
authResult: ({ data, referrer }: ContentMessageWindowEventParams) => void;
|
||||
webAuthnResult: ({ data, referrer }: ContentMessageWindowEventParams) => void;
|
||||
duoResult: ({ data, referrer }: ContentMessageWindowEventParams) => void;
|
||||
};
|
||||
|
||||
export {
|
||||
|
@ -60,6 +60,19 @@ describe("ContentMessageHandler", () => {
|
||||
referrer: "localhost",
|
||||
});
|
||||
});
|
||||
|
||||
it("sends a duoResult message", () => {
|
||||
const mockCode = "mockCode";
|
||||
const command = "duoResult";
|
||||
|
||||
postWindowMessage({ command: command, code: mockCode });
|
||||
|
||||
expect(sendMessageSpy).toHaveBeenCalledWith({
|
||||
command: command,
|
||||
code: mockCode,
|
||||
referrer: "localhost",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("handled extension messages", () => {
|
||||
|
@ -24,6 +24,8 @@ const windowMessageHandlers: ContentMessageWindowEventHandlers = {
|
||||
handleAuthResultMessage(data, referrer),
|
||||
webAuthnResult: ({ data, referrer }: { data: any; referrer: string }) =>
|
||||
handleWebAuthnResultMessage(data, referrer),
|
||||
duoResult: ({ data, referrer }: { data: any; referrer: string }) =>
|
||||
handleDuoResultMessage(data, referrer),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -37,6 +39,17 @@ async function handleAuthResultMessage(data: ContentMessageWindowData, referrer:
|
||||
await chrome.runtime.sendMessage({ command, code, state, lastpass, referrer });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the Duo 2FA result message from the window.
|
||||
*
|
||||
* @param data - Data from the window message
|
||||
* @param referrer - The referrer of the window
|
||||
*/
|
||||
async function handleDuoResultMessage(data: ContentMessageWindowData, referrer: string) {
|
||||
const { command, code } = data;
|
||||
await chrome.runtime.sendMessage({ command, code: code, referrer });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the webauthn result message from the window.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@ import { BitwardenToastModule } from "@bitwarden/angular/components/toastr.compo
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe";
|
||||
import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe";
|
||||
import { AvatarModule } from "@bitwarden/components";
|
||||
import { AvatarModule, ButtonModule } from "@bitwarden/components";
|
||||
|
||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
||||
@ -106,6 +106,7 @@ import "../platform/popup/locales";
|
||||
FilePopoutCalloutComponent,
|
||||
AvatarModule,
|
||||
AccountComponent,
|
||||
ButtonModule,
|
||||
],
|
||||
declarations: [
|
||||
ActionButtonsComponent,
|
||||
|
@ -10,9 +10,7 @@
|
||||
<div
|
||||
class="col-5"
|
||||
[ngClass]="{
|
||||
'col-9':
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
'col-9': !duoFrameless && isDuoProvider
|
||||
}"
|
||||
>
|
||||
<p class="lead text-center mb-4">{{ title }}</p>
|
||||
@ -83,12 +81,16 @@
|
||||
<iframe id="webauthn_iframe" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<!-- Duo -->
|
||||
<ng-container *ngIf="isDuoProvider">
|
||||
<ng-container *ngIf="duoFrameless">
|
||||
<p *ngIf="selectedProviderType === providerType.OrganizationDuo" class="tw-mb-0">
|
||||
{{ "duoRequiredByOrgForAccount" | i18n }}
|
||||
</p>
|
||||
<p>{{ "launchDuoAndFollowStepsToFinishLoggingIn" | i18n }}</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!duoFrameless">
|
||||
<div id="duo-frame" class="mb-3">
|
||||
<iframe
|
||||
id="duo_iframe"
|
||||
@ -96,6 +98,7 @@
|
||||
></iframe>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<i
|
||||
class="bwi bwi-spinner text-muted bwi-spin pull-right"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
@ -124,15 +127,15 @@
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
></iframe>
|
||||
</div>
|
||||
<div class="d-flex mb-3">
|
||||
<!-- Buttons -->
|
||||
<div class="tw-flex tw-flex-col tw-mb-3">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-block btn-submit"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo &&
|
||||
!isDuoProvider &&
|
||||
selectedProviderType !== providerType.WebAuthn
|
||||
"
|
||||
>
|
||||
@ -145,7 +148,16 @@
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||
<button
|
||||
(click)="launchDuoFrameless()"
|
||||
type="button"
|
||||
class="btn btn-primary btn-block"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="duoFrameless && isDuoProvider"
|
||||
>
|
||||
<span> {{ "launchDuo" | i18n }} </span>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block">
|
||||
{{ "cancel" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Inject, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, Inject, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component";
|
||||
@ -25,7 +25,7 @@ import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||
templateUrl: "two-factor.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDestroy {
|
||||
@ViewChild("twoFactorOptions", { read: ViewContainerRef, static: true })
|
||||
twoFactorOptionsModal: ViewContainerRef;
|
||||
|
||||
@ -104,4 +104,28 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private duoResultChannel: BroadcastChannel;
|
||||
|
||||
protected override setupDuoResultListener() {
|
||||
if (!this.duoResultChannel) {
|
||||
this.duoResultChannel = new BroadcastChannel("duoResult");
|
||||
this.duoResultChannel.addEventListener("message", this.handleDuoResultMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private handleDuoResultMessage = async (msg: { data: { code: string } }) => {
|
||||
this.token = msg.data.code;
|
||||
await this.submit();
|
||||
};
|
||||
|
||||
async ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
|
||||
if (this.duoResultChannel) {
|
||||
// clean up duo listener if it was initialized.
|
||||
this.duoResultChannel.removeEventListener("message", this.handleDuoResultMessage);
|
||||
this.duoResultChannel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5918,6 +5918,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"launchDuoAndFollowStepsToFinishLoggingIn": {
|
||||
"message": "Launch DUO and follow the steps to finish logging in."
|
||||
},
|
||||
"duoRequiredByOrgForAccount": {
|
||||
"message": "DUO two-step login is required for your account."
|
||||
},
|
||||
"launchDuo": {
|
||||
"message": "Launch DUO"
|
||||
},
|
||||
"turnOn": {
|
||||
"message": "Turn on"
|
||||
},
|
||||
|
@ -44,6 +44,11 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
formPromise: Promise<any>;
|
||||
emailPromise: Promise<any>;
|
||||
orgIdentifier: string = null;
|
||||
|
||||
duoFrameless = false;
|
||||
duoFramelessUrl: string = null;
|
||||
duoResultListenerInitialized = false;
|
||||
|
||||
onSuccessfulLogin: () => Promise<void>;
|
||||
onSuccessfulLoginNavigate: () => Promise<void>;
|
||||
|
||||
@ -57,6 +62,13 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
protected forcePasswordResetRoute = "update-temp-password";
|
||||
protected successRoute = "vault";
|
||||
|
||||
get isDuoProvider(): boolean {
|
||||
return (
|
||||
this.selectedProviderType === TwoFactorProviderType.Duo ||
|
||||
this.selectedProviderType === TwoFactorProviderType.OrganizationDuo
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected authService: AuthService,
|
||||
protected router: Router,
|
||||
@ -148,6 +160,26 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
break;
|
||||
case TwoFactorProviderType.Duo:
|
||||
case TwoFactorProviderType.OrganizationDuo:
|
||||
// 2 Duo 2FA flows available
|
||||
// 1. Duo Web SDK (iframe) - existing, to be deprecated
|
||||
// 2. Duo Frameless (new tab) - new
|
||||
|
||||
// AuthUrl only exists for new Duo Frameless flow
|
||||
if (providerData.AuthUrl) {
|
||||
this.duoFrameless = true;
|
||||
// Setup listener for duo-redirect.ts connector to send back the code
|
||||
|
||||
if (!this.duoResultListenerInitialized) {
|
||||
// setup client specific duo result listener
|
||||
this.setupDuoResultListener();
|
||||
this.duoResultListenerInitialized = true;
|
||||
}
|
||||
|
||||
// flow must be launched by user so they can choose to remember the device or not.
|
||||
this.duoFramelessUrl = providerData.AuthUrl;
|
||||
} else {
|
||||
// Duo Web SDK (iframe) flow
|
||||
// TODO: remove when we remove the "duo-redirect" feature flag
|
||||
setTimeout(() => {
|
||||
DuoWebSDK.init({
|
||||
iframe: undefined,
|
||||
@ -162,6 +194,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
case TwoFactorProviderType.Email:
|
||||
this.twoFactorEmail = providerData.Email;
|
||||
@ -231,6 +265,9 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
return true;
|
||||
}
|
||||
|
||||
// Each client will have own implementation
|
||||
protected setupDuoResultListener(): void {}
|
||||
|
||||
private async handleLoginResponse(authResult: AuthResult) {
|
||||
if (this.handleCaptchaRequired(authResult)) {
|
||||
return;
|
||||
@ -449,4 +486,9 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
get needsLock(): boolean {
|
||||
return this.authService.authingWithSso() || this.authService.authingWithUserApiKey();
|
||||
}
|
||||
|
||||
launchDuoFrameless() {
|
||||
// Launch Duo Frameless flow in new tab
|
||||
this.platformUtilsService.launchUri(this.duoFramelessUrl);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user