1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-26 05:03:33 +00:00

refactor(auth): [PM-9678] remove deprecated login components

Removes the V1 Login components and related UnauthenticatedExtensionUIRefresh
feature flag functions. Part of the authentication UI modernization efforts.

Closes PM-9678
This commit is contained in:
Alec Rippberger
2025-03-12 14:36:05 -05:00
committed by GitHub
parent e268055dc1
commit 4308cd8a9f
15 changed files with 12 additions and 1693 deletions

View File

@@ -1,36 +0,0 @@
<app-header [noTheme]="true"></app-header>
<div class="center-content">
<div class="content login-page">
<div class="logo-image"></div>
<p class="lead text-center">{{ "loginOrCreateNewAccount" | i18n }}</p>
<form #form [formGroup]="formGroup" (ngSubmit)="submit()">
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{ "emailAddress" | i18n }}</label>
<input id="email" type="email" formControlName="email" appInputVerbatim="false" />
</div>
<environment-selector></environment-selector>
<div class="remember-email-check">
<input
id="rememberEmail"
type="checkbox"
name="rememberEmail"
formControlName="rememberEmail"
/>
<label for="rememberEmail">{{ "rememberEmail" | i18n }}</label>
</div>
</div>
</div>
<div class="box">
<button type="submit" class="btn primary block">
<b>{{ "continue" | i18n }}</b>
</button>
</div>
</form>
<p class="createAccountLink">
{{ "newAroundHere" | i18n }}
<a routerLink="/signup" (click)="setLoginEmailValues()">{{ "createAccount" | i18n }}</a>
</p>
</div>
</div>

View File

@@ -1,130 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
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 } 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";
import { AccountSwitcherService } from "./account-switching/services/account-switcher.service";
@Component({
selector: "app-home",
templateUrl: "home.component.html",
})
export class HomeComponent implements OnInit, OnDestroy {
@ViewChild(EnvironmentSelectorComponent, { static: true })
environmentSelector!: EnvironmentSelectorComponent;
private destroyed$: Subject<void> = new Subject();
loginInitiated = false;
formGroup = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
rememberEmail: [false],
});
constructor(
protected platformUtilsService: PlatformUtilsService,
private formBuilder: FormBuilder,
private router: Router,
private i18nService: I18nService,
private loginEmailService: LoginEmailServiceAbstraction,
private accountSwitcherService: AccountSwitcherService,
private toastService: ToastService,
private configService: ConfigService,
private route: ActivatedRoute,
) {}
async ngOnInit(): Promise<void> {
this.listenForUnauthUiRefreshFlagChanges();
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
const rememberEmail = this.loginEmailService.getRememberEmail();
if (email != null) {
this.formGroup.patchValue({ email, rememberEmail });
} else {
const storedEmail = await firstValueFrom(this.loginEmailService.storedEmail$);
if (storedEmail != null) {
this.formGroup.patchValue({ email: storedEmail, rememberEmail: true });
}
}
this.environmentSelector.onOpenSelfHostedSettings
.pipe(
switchMap(async () => {
await this.setLoginEmailValues();
await this.router.navigate(["environment"]);
}),
takeUntil(this.destroyed$),
)
.subscribe();
}
ngOnDestroy(): void {
this.destroyed$.next();
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 qParams = await firstValueFrom(this.route.queryParams);
const uniqueQueryParams = {
...qParams,
// 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$;
}
async submit() {
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
message: this.i18nService.t("invalidEmail"),
});
return;
}
await this.setLoginEmailValues();
await this.router.navigate(["login"], {
queryParams: { email: this.formGroup.controls.email.value },
});
}
async setLoginEmailValues() {
// Note: Browser saves email settings here instead of the login component
this.loginEmailService.setRememberEmail(this.formGroup.controls.rememberEmail.value);
await this.loginEmailService.setLoginEmail(this.formGroup.controls.email.value);
await this.loginEmailService.saveEmailSettings();
}
}

View File

@@ -1,81 +0,0 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
<header>
<h1 class="login-center">
<span class="title">{{ "logIn" | i18n }}</span>
</h1>
</header>
<main tabindex="-1">
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<input id="email" type="text" formControlName="email" [hidden]="true" />
<input
id="rememberEmail"
type="checkbox"
formControlName="rememberEmail"
[hidden]="true"
/>
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
<input
id="masterPassword"
type="{{ showPassword ? 'text' : 'password' }}"
class="monospaced"
formControlName="masterPassword"
appInputVerbatim
appAutofocus
/>
</div>
<div class="action-buttons">
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePassword()"
[attr.aria-pressed]="showPassword"
>
<i
class="bwi bwi-lg"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
aria-hidden="true"
></i>
</button>
</div>
</div>
<div class="box-content-row" [hidden]="!showCaptcha()">
<iframe
id="hcaptcha_iframe"
height="80"
sandbox="allow-scripts allow-same-origin"
></iframe>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn link" routerLink="/hint" (click)="saveEmailSettings()">
<b>{{ "getMasterPasswordHint" | i18n }}</b>
</button>
</div>
</div>
<div class="content login-buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading">
<span [hidden]="form.loading"
><b>{{ "logInWithMasterPassword" | i18n }}</b></span
>
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<div class="tw-mb-3" *ngIf="showLoginWithDevice">
<button type="button" class="btn block" (click)="startAuthRequestLogin()">
<span> <i class="bwi bwi-mobile"></i> {{ "loginWithDevice" | i18n }} </span>
</button>
</div>
<button type="button" (click)="launchSsoBrowser()" class="btn block">
<i class="bwi bwi-provider" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
</button>
<div class="small">
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ loggedEmail }}</p>
<a routerLink="/home">{{ "notYou" | i18n }}</a>
</div>
</div>
</main>
</form>

View File

@@ -1,142 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, NgZone, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { firstValueFrom } 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";
import {
LoginStrategyServiceAbstraction,
LoginEmailServiceAbstraction,
} from "@bitwarden/auth/common";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.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";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
@Component({
selector: "app-login",
templateUrl: "login-v1.component.html",
})
export class LoginComponentV1 extends BaseLoginComponent implements OnInit {
constructor(
devicesApiService: DevicesApiServiceAbstraction,
appIdService: AppIdService,
loginStrategyService: LoginStrategyServiceAbstraction,
router: Router,
protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService,
protected stateService: StateService,
protected environmentService: EnvironmentService,
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
protected cryptoFunctionService: CryptoFunctionService,
syncService: SyncService,
logService: LogService,
ngZone: NgZone,
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService,
route: ActivatedRoute,
loginEmailService: LoginEmailServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
toastService: ToastService,
) {
super(
devicesApiService,
appIdService,
loginStrategyService,
router,
platformUtilsService,
i18nService,
stateService,
environmentService,
passwordGenerationService,
cryptoFunctionService,
logService,
ngZone,
formBuilder,
formValidationErrorService,
route,
loginEmailService,
ssoLoginService,
toastService,
);
this.onSuccessfulLogin = async () => {
await syncService.fullSync(true);
};
this.successRoute = "/tabs/vault";
}
async ngOnInit(): Promise<void> {
await super.ngOnInit();
await this.validateEmail();
}
settings() {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["environment"]);
}
async launchSsoBrowser() {
// Save off email for SSO
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
// Generate necessary sso params
const passwordOptions: any = {
type: "password",
length: 64,
uppercase: true,
lowercase: true,
numbers: true,
special: false,
};
const state =
(await this.passwordGenerationService.generatePassword(passwordOptions)) +
":clientId=browser";
const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256");
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
await this.ssoLoginService.setCodeVerifier(codeVerifier);
await this.ssoLoginService.setSsoState(state);
const env = await firstValueFrom(this.environmentService.environment$);
let url = env.getWebVaultUrl();
if (url == null) {
url = "https://vault.bitwarden.com";
}
const redirectUri = url + "/sso-connector.html";
// Launch browser
this.platformUtilsService.launchUri(
url +
"/#/sso?clientId=browser" +
"&redirectUri=" +
encodeURIComponent(redirectUri) +
"&state=" +
state +
"&codeChallenge=" +
codeChallenge +
"&email=" +
encodeURIComponent(this.formGroup.controls.email.value),
);
}
async saveEmailSettings() {
// values should be saved on home component
return;
}
}

View File

@@ -22,9 +22,7 @@ import { CurrentAccountComponent } from "../auth/popup/account-switching/current
import { EnvironmentComponent } from "../auth/popup/environment.component";
import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component";
import { HintComponent } from "../auth/popup/hint.component";
import { HomeComponent } from "../auth/popup/home.component";
import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component";
import { LoginComponentV1 } from "../auth/popup/login-v1.component";
import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component";
import { RemovePasswordComponent } from "../auth/popup/remove-password.component";
import { SetPasswordComponent } from "../auth/popup/set-password.component";
@@ -98,9 +96,7 @@ import "../platform/popup/locales";
ColorPasswordCountPipe,
EnvironmentComponent,
HintComponent,
HomeComponent,
LoginViaAuthRequestComponentV1,
LoginComponentV1,
LoginDecryptionOptionsComponentV1,
SetPasswordComponent,
SsoComponentV1,