1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-2417] Update LoginApprovalComponent on Desktop (#6751)

* [PM-2417] convert modal to dialog service

* code format

* [PM-2417] Fix title

* [PM-2417] Remove unnecessary class

* Updated to use a local reference for the dialog.

* Changes to clarify the method naming

* More cleanup with Will.

* Removed unused style

---------

Co-authored-by: Todd Martin <tmartin@bitwarden.com>
Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com>
This commit is contained in:
André Bispo
2024-01-05 19:46:57 +00:00
committed by GitHub
parent 167648e213
commit 2a338319ea
4 changed files with 89 additions and 84 deletions

View File

@@ -399,7 +399,11 @@ export class AppComponent implements OnInit, OnDestroy {
break; break;
case "openLoginApproval": case "openLoginApproval":
if (message.notificationId != null) { if (message.notificationId != null) {
await this.openLoginApproval(message.notificationId); this.dialogService.closeAll();
const dialogRef = LoginApprovalComponent.open(this.dialogService, {
notificationId: message.notificationId,
});
await firstValueFrom(dialogRef.closed);
} }
break; break;
case "redrawMenu": case "redrawMenu":
@@ -473,19 +477,6 @@ export class AppComponent implements OnInit, OnDestroy {
}); });
} }
async openLoginApproval(notificationId: string) {
this.modalService.closeAll();
this.modal = await this.modalService.open(LoginApprovalComponent, {
data: { notificationId: notificationId },
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private async updateAppMenu() { private async updateAppMenu() {
let updateRequest: MenuUpdateRequest; let updateRequest: MenuUpdateRequest;
const stateAccounts = await firstValueFrom(this.stateService.accounts$); const stateAccounts = await firstValueFrom(this.stateService.accounts$);

View File

@@ -14,7 +14,6 @@ import { DeleteAccountComponent } from "../auth/delete-account.component";
import { EnvironmentComponent } from "../auth/environment.component"; import { EnvironmentComponent } from "../auth/environment.component";
import { HintComponent } from "../auth/hint.component"; import { HintComponent } from "../auth/hint.component";
import { LockComponent } from "../auth/lock.component"; import { LockComponent } from "../auth/lock.component";
import { LoginApprovalComponent } from "../auth/login/login-approval.component";
import { LoginModule } from "../auth/login/login.module"; import { LoginModule } from "../auth/login/login.module";
import { RegisterComponent } from "../auth/register.component"; import { RegisterComponent } from "../auth/register.component";
import { RemovePasswordComponent } from "../auth/remove-password.component"; import { RemovePasswordComponent } from "../auth/remove-password.component";
@@ -101,7 +100,6 @@ import { SendComponent } from "./tools/send/send.component";
VaultTimeoutInputComponent, VaultTimeoutInputComponent,
ViewComponent, ViewComponent,
ViewCustomFieldsComponent, ViewCustomFieldsComponent,
LoginApprovalComponent,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })

View File

@@ -1,43 +1,42 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="loginApprovalTitle"> <bit-dialog>
<div class="modal-dialog modal-md" role="document"> <span bitDialogTitle>{{ "areYouTryingtoLogin" | i18n }}</span>
<div id="login-approval-page" class="modal-content"> <ng-container bitDialogContent>
<div class="section-title"> <h4>{{ "logInAttemptBy" | i18n: email }}</h4>
<p style="text-transform: uppercase">{{ "areYouTryingtoLogin" | i18n }}</p> <div>
</div> <b>{{ "fingerprintPhraseHeader" | i18n }}</b>
<div class="content"> <p class="tw-text-code">{{ fingerprintPhrase }}</p>
<div class="section">
<h4>{{ "logInAttemptBy" | i18n: email }}</h4>
</div>
<div class="section">
<h4 class="label">{{ "fingerprintPhraseHeader" | i18n }}</h4>
<code>{{ fingerprintPhrase }}</code>
</div>
<div class="section">
<h4 class="label">{{ "deviceType" | i18n }}</h4>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div class="section">
<h4 class="label">{{ "ipAddress" | i18n }}</h4>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div class="section">
<h4 class="label">{{ "time" | i18n }}</h4>
<p>{{ requestTimeText }}</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="primary" (click)="approveLogin(true, true)">
{{ "confirmLogIn" | i18n }}
</button>
<button type="button" (click)="approveLogin(false, true)">
{{ "denyLogIn" | i18n }}
</button>
</div>
</div> </div>
</div> <div>
</div> <b>{{ "deviceType" | i18n }}</b>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div>
<b>{{ "ipAddress" | i18n }}</b>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div>
<b>{{ "time" | i18n }}</b>
<p>{{ requestTimeText }}</p>
</div>
</ng-container>
<ng-container bitDialogFooter>
<button
bitButton
type="button"
buttonType="primary"
[bitAction]="approveLogin"
[bitDialogClose]="true"
>
{{ "confirmLogIn" | i18n }}
</button>
<button
bitButton
type="button"
buttonType="secondary"
[bitAction]="denyLogin"
[bitDialogClose]="true"
>
{{ "denyLogIn" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -1,8 +1,9 @@
import { Component, OnInit, OnDestroy } from "@angular/core"; import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { Subject } from "rxjs"; import { CommonModule } from "@angular/common";
import { Component, OnInit, OnDestroy, Inject } from "@angular/core";
import { Subject, firstValueFrom } from "rxjs";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
@@ -12,13 +13,25 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import {
AsyncActionsModule,
ButtonModule,
DialogModule,
DialogService,
} from "@bitwarden/components";
const RequestTimeOut = 60000 * 15; //15 Minutes const RequestTimeOut = 60000 * 15; //15 Minutes
const RequestTimeUpdate = 60000 * 5; //5 Minutes const RequestTimeUpdate = 60000 * 5; //5 Minutes
export interface LoginApprovalDialogParams {
notificationId: string;
}
@Component({ @Component({
selector: "login-approval", selector: "login-approval",
templateUrl: "login-approval.component.html", templateUrl: "login-approval.component.html",
standalone: true,
imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule],
}) })
export class LoginApprovalComponent implements OnInit, OnDestroy { export class LoginApprovalComponent implements OnInit, OnDestroy {
notificationId: string; notificationId: string;
@@ -30,9 +43,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
authRequestResponse: AuthRequestResponse; authRequestResponse: AuthRequestResponse;
interval: NodeJS.Timeout; interval: NodeJS.Timeout;
requestTimeText: string; requestTimeText: string;
dismissModal: boolean;
constructor( constructor(
@Inject(DIALOG_DATA) private params: LoginApprovalDialogParams,
protected stateService: StateService, protected stateService: StateService,
protected platformUtilsService: PlatformUtilsService, protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService, protected i18nService: I18nService,
@@ -40,23 +53,17 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
protected authService: AuthService, protected authService: AuthService,
protected appIdService: AppIdService, protected appIdService: AppIdService,
protected cryptoService: CryptoService, protected cryptoService: CryptoService,
private modalRef: ModalRef, private dialogRef: DialogRef,
config: ModalConfig,
) { ) {
this.notificationId = config.data.notificationId; this.notificationId = params.notificationId;
this.dismissModal = true;
this.modalRef.onClosed
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
.subscribe(() => {
if (this.dismissModal) {
this.approveLogin(false, false);
}
});
} }
ngOnDestroy(): void { async ngOnDestroy(): Promise<void> {
clearInterval(this.interval); clearInterval(this.interval);
const closedWithButton = await firstValueFrom(this.dialogRef.closed);
if (!closedWithButton) {
this.retrieveAuthRequestAndRespond(false);
}
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
@@ -86,14 +93,24 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
} }
} }
async approveLogin(approveLogin: boolean, approveDenyButtonClicked: boolean) { /**
clearInterval(this.interval); * Strongly-typed helper to open a LoginApprovalDialog
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param data Configuration for the dialog
*/
static open(dialogService: DialogService, data: LoginApprovalDialogParams) {
return dialogService.open(LoginApprovalComponent, { data });
}
this.dismissModal = !approveDenyButtonClicked; denyLogin = async () => {
if (approveDenyButtonClicked) { await this.retrieveAuthRequestAndRespond(false);
this.modalRef.close(); };
}
approveLogin = async () => {
await this.retrieveAuthRequestAndRespond(true);
};
private async retrieveAuthRequestAndRespond(approve: boolean) {
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId); this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
if (this.authRequestResponse.requestApproved || this.authRequestResponse.responseDate != null) { if (this.authRequestResponse.requestApproved || this.authRequestResponse.responseDate != null) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
@@ -105,7 +122,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
const loginResponse = await this.authService.passwordlessLogin( const loginResponse = await this.authService.passwordlessLogin(
this.authRequestResponse.id, this.authRequestResponse.id,
this.authRequestResponse.publicKey, this.authRequestResponse.publicKey,
approveLogin, approve,
); );
this.showResultToast(loginResponse); this.showResultToast(loginResponse);
} }
@@ -165,7 +182,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
); );
} else { } else {
clearInterval(this.interval); clearInterval(this.interval);
this.modalRef.close(); this.dialogRef.close();
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"info", "info",
null, null,