1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

fix(login): [PM-20587] Fix unawaited calls to set login email

* Await setting login email in state.

* Changed to get email state within the component.

* Added null filter

* PM-20587 - LoginViaAuthRequest component - update initStandardAuthRequestFlow to correctly retrieve data from active account.

---------

Co-authored-by: Jared Snider <jsnider@bitwarden.com>
This commit is contained in:
Todd Martin
2025-04-29 12:00:02 -04:00
committed by GitHub
parent 417b59a1cc
commit 29d0e74e23
4 changed files with 23 additions and 6 deletions

View File

@@ -284,7 +284,6 @@ export class LoginDecryptionOptionsComponent implements OnInit {
}
protected async approveFromOtherDevice() {
this.loginEmailService.setLoginEmail(this.email);
await this.router.navigate(["/login-with-device"]);
}
@@ -297,7 +296,6 @@ export class LoginDecryptionOptionsComponent implements OnInit {
}
protected async requestAdminApproval() {
this.loginEmailService.setLoginEmail(this.email);
await this.router.navigate(["/admin-approval-requested"]);
}

View File

@@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { IsActiveMatchOptions, Router, RouterModule } from "@angular/router";
import { firstValueFrom, map } from "rxjs";
import { Observable, filter, firstValueFrom, map, merge, race, take, timer } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -178,7 +178,26 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
private async initStandardAuthRequestFlow(): Promise<void> {
this.flow = Flow.StandardAuthRequest;
this.email = (await firstValueFrom(this.loginEmailService.loginEmail$)) || undefined;
// For a standard flow, we can get the user's email from two different places:
// 1. The loginEmailService, which is the email that the user is trying to log in with. This is cleared
// when the user logs in successfully. We can use this when the user is using Login with Device.
// 2. With TDE Login with Another Device, the user is already logged in and we just need to get
// a decryption key, so we can use the active account's email.
const activeAccountEmail$: Observable<string | undefined> =
this.accountService.activeAccount$.pipe(map((a) => a?.email));
const loginEmail$: Observable<string | null> = this.loginEmailService.loginEmail$;
// Use merge as we want to get the first value from either observable.
const firstEmail$ = merge(loginEmail$, activeAccountEmail$).pipe(
filter((e): e is string => !!e), // convert null/undefined to false and filter out so we narrow type to string
take(1), // complete after first value
);
const emailRetrievalTimeout$ = timer(2500).pipe(map(() => undefined as undefined));
// Wait for either the first email or the timeout to occur so we can proceed
// neither above observable will complete, so we have to add a timeout
this.email = await firstValueFrom(race(firstEmail$, emailRetrievalTimeout$));
if (!this.email) {
await this.handleMissingEmail();

View File

@@ -539,7 +539,7 @@ export class LoginComponent implements OnInit, OnDestroy {
// If we load an email into the form, we need to initialize it for the login process as well
// so that other login components can use it.
// We do this here as it's possible that a user doesn't edit the email field before submitting.
this.loginEmailService.setLoginEmail(storedEmail);
await this.loginEmailService.setLoginEmail(storedEmail);
} else {
this.formGroup.controls.rememberEmail.setValue(false);
}

View File

@@ -79,7 +79,7 @@ export class PasswordHintComponent implements OnInit {
};
protected async cancel() {
this.loginEmailService.setLoginEmail(this.email);
await this.loginEmailService.setLoginEmail(this.email);
await this.router.navigate(["login"]);
}