[PM-7084] 6/6: Introduce shared duo two-factor component (#9772)
* Add shared duo component * Fix duo import * Fix wrong i18n service DI in duo desktop component * Remove duo v2 * Add override to functions * Remove web duo implementation * Update apps/browser/src/auth/popup/two-factor-auth-duo.component.ts Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com> * Update apps/desktop/src/auth/two-factor-auth-duo.component.ts Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com> * Update libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component.ts Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com> * Fix missing service on duo components * Fix missing service on base duo auth component * Fix constructor super calls in duo auth component * Fix duo auth components incorrectly extending base class --------- Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com>
This commit is contained in:
parent
bc1ee0a169
commit
05e8b45edb
|
@ -0,0 +1,105 @@
|
||||||
|
import { DialogModule } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule, FormsModule } from "@angular/forms";
|
||||||
|
import { Subject, Subscription, filter, firstValueFrom, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent as TwoFactorAuthDuoBaseComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-duo.component";
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { AsyncActionsModule } from "../../../../../libs/components/src/async-actions";
|
||||||
|
import { ButtonModule } from "../../../../../libs/components/src/button";
|
||||||
|
import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
||||||
|
import { LinkModule } from "../../../../../libs/components/src/link";
|
||||||
|
import { I18nPipe } from "../../../../../libs/components/src/shared/i18n.pipe";
|
||||||
|
import { TypographyModule } from "../../../../../libs/components/src/typography";
|
||||||
|
import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "app-two-factor-auth-duo",
|
||||||
|
templateUrl:
|
||||||
|
"../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
providers: [I18nPipe],
|
||||||
|
})
|
||||||
|
export class TwoFactorAuthDuoComponent extends TwoFactorAuthDuoBaseComponent {
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
duoResultSubscription: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
private browserMessagingApi: ZonedMessageListenerService,
|
||||||
|
private environmentService: EnvironmentService,
|
||||||
|
toastService: ToastService,
|
||||||
|
) {
|
||||||
|
super(i18nService, platformUtilsService, toastService);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
await super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
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; state: string }) => {
|
||||||
|
this.token.emit(msg.code + "|" + msg.state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override async launchDuoFrameless() {
|
||||||
|
if (this.duoFramelessUrl === null) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const duoHandOffMessage = {
|
||||||
|
title: this.i18nService.t("youSuccessfullyLoggedIn"),
|
||||||
|
message: this.i18nService.t("youMayCloseThisWindow"),
|
||||||
|
isCountdown: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// we're using the connector here as a way to set a cookie with translations
|
||||||
|
// before continuing to the duo frameless url
|
||||||
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
|
const launchUrl =
|
||||||
|
env.getWebVaultUrl() +
|
||||||
|
"/duo-redirect-connector.html" +
|
||||||
|
"?duoFramelessUrl=" +
|
||||||
|
encodeURIComponent(this.duoFramelessUrl) +
|
||||||
|
"&handOffMessage=" +
|
||||||
|
encodeURIComponent(JSON.stringify(duoHandOffMessage));
|
||||||
|
this.platformUtilsService.launchUri(launchUrl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import {
|
||||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
|
||||||
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -65,6 +66,7 @@ import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthDuoComponent,
|
||||||
TwoFactorAuthWebAuthnComponent,
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import { DialogModule } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, NgZone } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule, FormsModule } from "@angular/forms";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||||
|
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
FormFieldModule,
|
||||||
|
LinkModule,
|
||||||
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent as TwoFactorAuthDuoBaseComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component";
|
||||||
|
|
||||||
|
const BroadcasterSubscriptionId = "TwoFactorComponent";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "app-two-factor-auth-duo",
|
||||||
|
templateUrl:
|
||||||
|
"../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
providers: [I18nPipe],
|
||||||
|
})
|
||||||
|
export class TwoFactorAuthDuoComponent extends TwoFactorAuthDuoBaseComponent {
|
||||||
|
constructor(
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
private broadcasterService: BroadcasterService,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
private environmentService: EnvironmentService,
|
||||||
|
toastService: ToastService,
|
||||||
|
) {
|
||||||
|
super(i18nService, platformUtilsService, toastService);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
await super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
duoCallbackSubscriptionEnabled: boolean = false;
|
||||||
|
|
||||||
|
protected override setupDuoResultListener() {
|
||||||
|
if (!this.duoCallbackSubscriptionEnabled) {
|
||||||
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
|
await this.ngZone.run(async () => {
|
||||||
|
if (message.command === "duoCallback") {
|
||||||
|
this.token.emit(message.code + "|" + message.state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.duoCallbackSubscriptionEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override async launchDuoFrameless() {
|
||||||
|
if (this.duoFramelessUrl === null) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const duoHandOffMessage = {
|
||||||
|
title: this.i18nService.t("youSuccessfullyLoggedIn"),
|
||||||
|
message: this.i18nService.t("youMayCloseThisWindow"),
|
||||||
|
isCountdown: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// we're using the connector here as a way to set a cookie with translations
|
||||||
|
// before continuing to the duo frameless url
|
||||||
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
|
const launchUrl =
|
||||||
|
env.getWebVaultUrl() +
|
||||||
|
"/duo-redirect-connector.html" +
|
||||||
|
"?duoFramelessUrl=" +
|
||||||
|
encodeURIComponent(this.duoFramelessUrl) +
|
||||||
|
"&handOffMessage=" +
|
||||||
|
encodeURIComponent(JSON.stringify(duoHandOffMessage));
|
||||||
|
this.platformUtilsService.launchUri(launchUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnDestroy() {
|
||||||
|
if (this.duoCallbackSubscriptionEnabled) {
|
||||||
|
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||||
|
this.duoCallbackSubscriptionEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ import { LinkModule } from "../../../../libs/components/src/link";
|
||||||
import { I18nPipe } from "../../../../libs/components/src/shared/i18n.pipe";
|
import { I18nPipe } from "../../../../libs/components/src/shared/i18n.pipe";
|
||||||
import { TypographyModule } from "../../../../libs/components/src/typography";
|
import { TypographyModule } from "../../../../libs/components/src/typography";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl:
|
templateUrl:
|
||||||
|
@ -40,6 +42,7 @@ import { TypographyModule } from "../../../../libs/components/src/typography";
|
||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthDuoComponent,
|
||||||
TwoFactorAuthWebAuthnComponent,
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { DialogModule } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule, FormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent as TwoFactorAuthDuoBaseComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component";
|
||||||
|
import { AsyncActionsModule } from "../../../../../libs/components/src/async-actions";
|
||||||
|
import { ButtonModule } from "../../../../../libs/components/src/button";
|
||||||
|
import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
||||||
|
import { LinkModule } from "../../../../../libs/components/src/link";
|
||||||
|
import { TypographyModule } from "../../../../../libs/components/src/typography";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "app-two-factor-auth-duo",
|
||||||
|
templateUrl:
|
||||||
|
"../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-duo.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
providers: [I18nPipe],
|
||||||
|
})
|
||||||
|
export class TwoFactorAuthDuoComponent extends TwoFactorAuthDuoBaseComponent {
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
await super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
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; state: string } }) => {
|
||||||
|
this.token.emit(msg.data.code + "|" + msg.data.state);
|
||||||
|
};
|
||||||
|
|
||||||
|
async ngOnDestroy() {
|
||||||
|
if (this.duoResultChannel) {
|
||||||
|
// clean up duo listener if it was initialized.
|
||||||
|
this.duoResultChannel.removeEventListener("message", this.handleDuoResultMessage);
|
||||||
|
this.duoResultChannel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,8 @@ import { AsyncActionsModule } from "../../../../../libs/components/src/async-act
|
||||||
import { ButtonModule } from "../../../../../libs/components/src/button";
|
import { ButtonModule } from "../../../../../libs/components/src/button";
|
||||||
import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
||||||
|
|
||||||
|
import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl:
|
templateUrl:
|
||||||
|
@ -55,6 +57,7 @@ import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthDuoComponent,
|
||||||
TwoFactorAuthWebAuthnComponent,
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ng-container>
|
||||||
|
<p bitTypography="body1" class="tw-mb-0">
|
||||||
|
{{ "duoRequiredByOrgForAccount" | i18n }}
|
||||||
|
</p>
|
||||||
|
<p bitTypography="body1">{{ "launchDuoAndFollowStepsToFinishLoggingIn" | i18n }}</p>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { DialogModule } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule, FormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import {
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
ToastService,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "app-two-factor-auth-duo",
|
||||||
|
templateUrl: "two-factor-auth-duo.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
providers: [I18nPipe],
|
||||||
|
})
|
||||||
|
export class TwoFactorAuthDuoComponent {
|
||||||
|
@Output() token = new EventEmitter<string>();
|
||||||
|
@Input() providerData: any;
|
||||||
|
|
||||||
|
duoFramelessUrl: string = null;
|
||||||
|
duoResultListenerInitialized = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
protected toastService: ToastService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
await this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
// 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 = this.providerData.AuthUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each client will have own implementation
|
||||||
|
protected setupDuoResultListener(): void {}
|
||||||
|
async launchDuoFrameless(): Promise<void> {
|
||||||
|
if (this.duoFramelessUrl === null) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("duoHealthCheckResultsInNullAuthUrlError"),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.platformUtilsService.launchUri(this.duoFramelessUrl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,15 @@
|
||||||
(token)="token = $event; submitForm()"
|
(token)="token = $event; submitForm()"
|
||||||
*ngIf="selectedProviderType === providerType.WebAuthn"
|
*ngIf="selectedProviderType === providerType.WebAuthn"
|
||||||
/>
|
/>
|
||||||
|
<app-two-factor-auth-duo
|
||||||
|
(token)="token = $event; submitForm()"
|
||||||
|
[providerData]="providerData"
|
||||||
|
*ngIf="
|
||||||
|
selectedProviderType === providerType.OrganizationDuo ||
|
||||||
|
selectedProviderType === providerType.Duo
|
||||||
|
"
|
||||||
|
#duoComponent
|
||||||
|
/>
|
||||||
<bit-form-control *ngIf="selectedProviderType != null">
|
<bit-form-control *ngIf="selectedProviderType != null">
|
||||||
<bit-label>{{ "rememberMe" | i18n }}</bit-label>
|
<bit-label>{{ "rememberMe" | i18n }}</bit-label>
|
||||||
<input type="checkbox" bitCheckbox formControlName="remember" />
|
<input type="checkbox" bitCheckbox formControlName="remember" />
|
||||||
|
@ -35,10 +44,28 @@
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
bitButton
|
bitButton
|
||||||
bitFormButton
|
bitFormButton
|
||||||
*ngIf="selectedProviderType != null && selectedProviderType !== providerType.WebAuthn"
|
*ngIf="
|
||||||
|
selectedProviderType != null &&
|
||||||
|
selectedProviderType !== providerType.WebAuthn &&
|
||||||
|
selectedProviderType !== providerType.Duo &&
|
||||||
|
selectedProviderType !== providerType.OrganizationDuo
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ actionButtonText }} </span>
|
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ actionButtonText }} </span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
buttonType="primary"
|
||||||
|
bitButton
|
||||||
|
(click)="launchDuo()"
|
||||||
|
*ngIf="
|
||||||
|
selectedProviderType === providerType.Duo ||
|
||||||
|
selectedProviderType === providerType.OrganizationDuo
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "launchDuo" | i18n }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<a routerLink="/login" bitButton buttonType="secondary">
|
<a routerLink="/login" bitButton buttonType="secondary">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, Inject, OnInit } from "@angular/core";
|
import { Component, Inject, OnInit, ViewChild } from "@angular/core";
|
||||||
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
import { ActivatedRoute, NavigationExtras, Router, RouterLink } from "@angular/router";
|
import { ActivatedRoute, NavigationExtras, Router, RouterLink } from "@angular/router";
|
||||||
import { Subject, takeUntil, lastValueFrom, first, firstValueFrom } from "rxjs";
|
import { Subject, takeUntil, lastValueFrom, first, firstValueFrom } from "rxjs";
|
||||||
|
@ -39,6 +39,7 @@ import {
|
||||||
import { CaptchaProtectedComponent } from "../captcha-protected.component";
|
import { CaptchaProtectedComponent } from "../captcha-protected.component";
|
||||||
|
|
||||||
import { TwoFactorAuthAuthenticatorComponent } from "./two-factor-auth-authenticator.component";
|
import { TwoFactorAuthAuthenticatorComponent } from "./two-factor-auth-authenticator.component";
|
||||||
|
import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
|
||||||
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
||||||
import { TwoFactorAuthWebAuthnComponent } from "./two-factor-auth-webauthn.component";
|
import { TwoFactorAuthWebAuthnComponent } from "./two-factor-auth-webauthn.component";
|
||||||
import { TwoFactorAuthYubikeyComponent } from "./two-factor-auth-yubikey.component";
|
import { TwoFactorAuthYubikeyComponent } from "./two-factor-auth-yubikey.component";
|
||||||
|
@ -63,6 +64,7 @@ import {
|
||||||
TwoFactorOptionsComponent,
|
TwoFactorOptionsComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
|
TwoFactorAuthDuoComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
TwoFactorAuthWebAuthnComponent,
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
|
@ -78,6 +80,7 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements
|
||||||
selectedProviderType: TwoFactorProviderType = TwoFactorProviderType.Authenticator;
|
selectedProviderType: TwoFactorProviderType = TwoFactorProviderType.Authenticator;
|
||||||
providerData: any;
|
providerData: any;
|
||||||
|
|
||||||
|
@ViewChild("duoComponent") duoComponent!: TwoFactorAuthDuoComponent;
|
||||||
formGroup = this.formBuilder.group({
|
formGroup = this.formBuilder.group({
|
||||||
token: [
|
token: [
|
||||||
"",
|
"",
|
||||||
|
@ -220,6 +223,12 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async launchDuo() {
|
||||||
|
if (this.duoComponent != null) {
|
||||||
|
await this.duoComponent.launchDuoFrameless();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected handleMigrateEncryptionKey(result: AuthResult): boolean {
|
protected handleMigrateEncryptionKey(result: AuthResult): boolean {
|
||||||
if (!result.requiresEncryptionKeyMigration) {
|
if (!result.requiresEncryptionKeyMigration) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue