From b5450227da9d60bf72b709001fc4ce23c7115151 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:02:51 -0500 Subject: [PATCH] PM-14424 - LoginComponents should listen for unauthUiRefresh flag changes and forcibly change the UI on browser & desktop to make for a seamless experience without having to refresh. (#11830) --- apps/browser/src/auth/popup/home.component.ts | 33 +++++++++++++++++-- .../src/auth/login/login-v1.component.ts | 30 ++++++++++++++++- .../auth/src/angular/login/login.component.ts | 31 ++++++++++++++++- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/auth/popup/home.component.ts b/apps/browser/src/auth/popup/home.component.ts index cd9dfc3702..4d185fcbfc 100644 --- a/apps/browser/src/auth/popup/home.component.ts +++ b/apps/browser/src/auth/popup/home.component.ts @@ -1,10 +1,12 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; -import { Router } from "@angular/router"; -import { Subject, firstValueFrom, switchMap, takeUntil } from "rxjs"; +import { ActivatedRoute, Router } from "@angular/router"; +import { Subject, firstValueFrom, switchMap, takeUntil, tap } from "rxjs"; import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; import { LoginEmailServiceAbstraction, RegisterRouteService } from "@bitwarden/auth/common"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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"; @@ -38,9 +40,13 @@ export class HomeComponent implements OnInit, OnDestroy { private accountSwitcherService: AccountSwitcherService, private registerRouteService: RegisterRouteService, private toastService: ToastService, + private configService: ConfigService, + private route: ActivatedRoute, ) {} async ngOnInit(): Promise { + this.listenForUnauthUiRefreshFlagChanges(); + const email = await firstValueFrom(this.loginEmailService.loginEmail$); const rememberEmail = this.loginEmailService.getRememberEmail(); @@ -70,6 +76,29 @@ export class HomeComponent implements OnInit, OnDestroy { this.destroyed$.complete(); } + private listenForUnauthUiRefreshFlagChanges() { + this.configService + .getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh) + .pipe( + tap(async (flag) => { + // If the flag is turned ON, we must force a reload to ensure the correct UI is shown + if (flag) { + const uniqueQueryParams = { + ...this.route.queryParams, + // adding a unique timestamp to the query params to force a reload + t: new Date().getTime().toString(), + }; + + await this.router.navigate(["/login"], { + queryParams: uniqueQueryParams, + }); + } + }), + takeUntil(this.destroyed$), + ) + .subscribe(); + } + get availableAccounts$() { return this.accountSwitcherService.availableAccounts$; } diff --git a/apps/desktop/src/auth/login/login-v1.component.ts b/apps/desktop/src/auth/login/login-v1.component.ts index 6eb069d9bc..132b430f32 100644 --- a/apps/desktop/src/auth/login/login-v1.component.ts +++ b/apps/desktop/src/auth/login/login-v1.component.ts @@ -1,7 +1,7 @@ import { Component, NgZone, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; +import { Subject, takeUntil, tap } from "rxjs"; import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; @@ -14,8 +14,10 @@ import { import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -76,6 +78,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe webAuthnLoginService: WebAuthnLoginServiceAbstraction, registerRouteService: RegisterRouteService, toastService: ToastService, + private configService: ConfigService, ) { super( devicesApiService, @@ -105,6 +108,8 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe } async ngOnInit() { + this.listenForUnauthUiRefreshFlagChanges(); + await super.ngOnInit(); await this.getLoginWithDevice(this.loggedEmail); this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { @@ -137,6 +142,29 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe this.componentDestroyed$.complete(); } + private listenForUnauthUiRefreshFlagChanges() { + this.configService + .getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh) + .pipe( + tap(async (flag) => { + // If the flag is turned ON, we must force a reload to ensure the correct UI is shown + if (flag) { + const uniqueQueryParams = { + ...this.route.queryParams, + // adding a unique timestamp to the query params to force a reload + t: new Date().getTime().toString(), + }; + + await this.router.navigate(["/"], { + queryParams: uniqueQueryParams, + }); + } + }), + takeUntil(this.destroy$), + ) + .subscribe(); + } + async settings() { const [modal, childComponent] = await this.modalService.openViewRef( EnvironmentComponent, diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 239383ddd0..0193e4c403 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common"; import { Component, ElementRef, Input, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms"; import { ActivatedRoute, Router, RouterModule } from "@angular/router"; -import { firstValueFrom, Subject, take, takeUntil } from "rxjs"; +import { firstValueFrom, Subject, take, takeUntil, tap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { @@ -19,9 +19,11 @@ import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { ClientType, HttpStatusCode } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -139,12 +141,16 @@ export class LoginComponent implements OnInit, OnDestroy { private toastService: ToastService, private logService: LogService, private validationService: ValidationService, + private configService: ConfigService, ) { this.clientType = this.platformUtilsService.getClientType(); this.loginViaAuthRequestSupported = this.loginComponentService.isLoginViaAuthRequestSupported(); } async ngOnInit(): Promise { + // TODO: remove this when the UnauthenticatedExtensionUIRefresh feature flag is removed. + this.listenForUnauthUiRefreshFlagChanges(); + await this.defaultOnInit(); if (this.clientType === ClientType.Desktop) { @@ -162,6 +168,29 @@ export class LoginComponent implements OnInit, OnDestroy { this.destroy$.complete(); } + private listenForUnauthUiRefreshFlagChanges() { + this.configService + .getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh) + .pipe( + tap(async (flag) => { + // If the flag is turned OFF, we must force a reload to ensure the correct UI is shown + if (!flag) { + const uniqueQueryParams = { + ...this.activatedRoute.queryParams, + // adding a unique timestamp to the query params to force a reload + t: new Date().getTime().toString(), // Adding a unique timestamp as a query parameter + }; + + await this.router.navigate(["/"], { + queryParams: uniqueQueryParams, + }); + } + }), + takeUntil(this.destroy$), + ) + .subscribe(); + } + submit = async (): Promise => { if (this.clientType === ClientType.Desktop) { if (this.loginUiState !== LoginUiState.MASTER_PASSWORD_ENTRY) {