1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +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() { protected async approveFromOtherDevice() {
this.loginEmailService.setLoginEmail(this.email);
await this.router.navigate(["/login-with-device"]); await this.router.navigate(["/login-with-device"]);
} }
@@ -297,7 +296,6 @@ export class LoginDecryptionOptionsComponent implements OnInit {
} }
protected async requestAdminApproval() { protected async requestAdminApproval() {
this.loginEmailService.setLoginEmail(this.email);
await this.router.navigate(["/admin-approval-requested"]); 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 { Component, OnDestroy, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { IsActiveMatchOptions, Router, RouterModule } from "@angular/router"; 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 { JslibModule } from "@bitwarden/angular/jslib.module";
import { import {
@@ -178,7 +178,26 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
private async initStandardAuthRequestFlow(): Promise<void> { private async initStandardAuthRequestFlow(): Promise<void> {
this.flow = Flow.StandardAuthRequest; 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) { if (!this.email) {
await this.handleMissingEmail(); 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 // 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. // 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. // 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 { } else {
this.formGroup.controls.rememberEmail.setValue(false); this.formGroup.controls.rememberEmail.setValue(false);
} }

View File

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