mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-29 12:55:21 +01:00
Two-Step Login (#3852)
* [SG-163] Two step login flow web (#3648) * two step login flow * moved code from old branch and reafctored * fixed review comments * [SG-164] Two Step Login Flow - Browser (#3793) * Add new messages * Remove SSO button from home component * Change create account button to text * Add top padding to create account link * Add email input to HomeComponent * Add continue button to email input * Add form to home component * Retreive email from state service * Redirect to login after submit * Add error message for invalid email * Remove email input from login component * Remove loggingInTo from under MP input * Style the MP hint link * Add self hosted domain to email form * Made the mp hint link bold * Add the new login button * Style app-private-mode-warning in its component * Bitwarden -> Login text change * Remove the old login button * Cancel -> Close text change * Add avatar to login header * Login -> LoginWithMasterPassword text change * Add SSO button to login screen * Add not you button * Allow all clients to use the email query param on the login component * Introduct HomeGuard * Clear remembered email when clicking Not You * Make remember email opt-in * Use formGroup.patchValue instead of directly patching individual controls * [SG-165] Desktop login flow changes (#3814) * two step login flow * moved code from old branch and reafctored * fixed review comments * Make toggleValidateEmail in base class public * Add desktop login messages * Desktop login flow changes * Fix known device api error * Only submit if email has been validated * Clear remembered email when switching accounts * Fix merge issue * Add 'login with another device' button * Remove 'log in with another device' button for now * Pin login pag content to top instead of center justified * Leave email if 'Not you?' is clicked * Continue when enter is hit on email input Co-authored-by: gbubemismith <gsmithwalter@gmail.com> * [SG-750] and [SG-751] Web two step login bug fixes (#3843) * Continue when enter is hit on email input * Mark email input as touched on 'continue' so field is validated * disable login with device on self-hosted (#3895) * [SG-753] Keep email after hint component is launched in browser (#3883) * Keep email after hint component is launched in browser * Use query params instead of state for consistency * Send email and rememberEmail to home component on navigation (#3897) * removed avatar and close button from the password screen (#3901) * [SG-781] Remove extra login page and remove rememberEmail code (#3902) * Remove browser home guard * Always remember email for browser * Remove login landing page button * [SG-782] Add login service to streamline login form data persistence (#3911) * Add login service and abstraction * Inject login service into apps * Inject and use new service in login component * Use service in hint component to prefill email * Add method in LoginService to clear service values * Add LoginService to two-factor component to clear values * make login.service variables private Co-authored-by: Gbubemi Smith <gsmith@bitwarden.com> Co-authored-by: Addison Beck <addisonbeck1@gmail.com> Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: gbubemismith <gsmithwalter@gmail.com>
This commit is contained in:
parent
aa256b8a70
commit
2cd65939d5
@ -2028,5 +2028,20 @@
|
|||||||
"example": "Jun 15, 2015"
|
"example": "Jun 15, 2015"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"loginWithMasterPassword": {
|
||||||
|
"message": "Log in with master password"
|
||||||
|
},
|
||||||
|
"loggingInAs": {
|
||||||
|
"message": "Logging in as"
|
||||||
|
},
|
||||||
|
"notYou": {
|
||||||
|
"message": "Not you?"
|
||||||
|
},
|
||||||
|
"newAroundHere": {
|
||||||
|
"message": "New around here?"
|
||||||
|
},
|
||||||
|
"rememberEmail": {
|
||||||
|
"message": "Remember email"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
<header>
|
<header>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<button type="button" routerLink="/login">{{ "cancel" | i18n }}</button>
|
<button type="button" routerLink="/login">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="center">
|
<h1 class="center">
|
||||||
<span class="title">{{ "passwordHint" | i18n }}</span>
|
<span class="title">{{ "passwordHint" | i18n }}</span>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { HintComponent as BaseHintComponent } from "@bitwarden/angular/components/hint.component";
|
import { HintComponent as BaseHintComponent } from "@bitwarden/angular/components/hint.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -17,8 +18,14 @@ export class HintComponent extends BaseHintComponent {
|
|||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
logService: LogService
|
logService: LogService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
super(router, i18nService, apiService, platformUtilsService, logService, loginService);
|
||||||
|
|
||||||
|
super.onSuccessfulSubmit = async () => {
|
||||||
|
this.router.navigate([this.successRoute]);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,28 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="logo-image"></div>
|
<div class="logo-image"></div>
|
||||||
<p class="lead text-center">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
<p class="lead text-center">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||||
<button type="button" class="btn primary block" routerLink="/login">
|
<form #form [formGroup]="formGroup" (ngSubmit)="submit()">
|
||||||
<b>{{ "login" | i18n }}</b>
|
<div class="box">
|
||||||
</button>
|
<div class="box-content">
|
||||||
<button type="button" (click)="launchSsoBrowser()" class="btn block">
|
<div class="box-content-row" appBoxRow>
|
||||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||||
</button>
|
<input id="email" type="email" formControlName="email" appInputVerbatim="false" />
|
||||||
<button type="button" class="btn block" routerLink="/register">
|
</div>
|
||||||
{{ "createAccount" | i18n }}
|
</div>
|
||||||
</button>
|
<div class="box-footer no-margin" *ngIf="selfHostedDomain">
|
||||||
|
{{ "loggingInTo" | i18n: selfHostedDomain }}
|
||||||
|
</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="/register">{{ "createAccount" | i18n }}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" routerLink="/environment" class="settings-icon">
|
<button type="button" routerLink="/environment" class="settings-icon">
|
||||||
|
@ -1,63 +1,56 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
|
||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { Utils } from "@bitwarden/common/misc/utils";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-home",
|
selector: "app-home",
|
||||||
templateUrl: "home.component.html",
|
templateUrl: "home.component.html",
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent implements OnInit {
|
||||||
|
loginInitiated = false;
|
||||||
|
|
||||||
|
formGroup = this.formBuilder.group({
|
||||||
|
email: ["", [Validators.required, Validators.email]],
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
private passwordGenerationService: PasswordGenerationService,
|
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private cryptoFunctionService: CryptoFunctionService,
|
private formBuilder: FormBuilder,
|
||||||
private environmentService: EnvironmentService
|
private router: Router,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private environmentService: EnvironmentService,
|
||||||
|
private route: ActivatedRoute
|
||||||
) {}
|
) {}
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
const rememberedEmail = await this.stateService.getRememberedEmail();
|
||||||
|
if (rememberedEmail != null) {
|
||||||
|
this.formGroup.patchValue({ email: await this.stateService.getRememberedEmail() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async launchSsoBrowser() {
|
submit() {
|
||||||
// Generate necessary sso params
|
this.formGroup.markAllAsTouched();
|
||||||
const passwordOptions: any = {
|
if (this.formGroup.invalid) {
|
||||||
type: "password",
|
this.platformUtilsService.showToast(
|
||||||
length: 64,
|
"error",
|
||||||
uppercase: true,
|
this.i18nService.t("errorOccured"),
|
||||||
lowercase: true,
|
this.i18nService.t("invalidEmail")
|
||||||
numbers: true,
|
);
|
||||||
special: false,
|
return;
|
||||||
};
|
|
||||||
|
|
||||||
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.stateService.setSsoCodeVerifier(codeVerifier);
|
|
||||||
await this.stateService.setSsoState(state);
|
|
||||||
|
|
||||||
let url = this.environmentService.getWebVaultUrl();
|
|
||||||
if (url == null) {
|
|
||||||
url = "https://vault.bitwarden.com";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirectUri = url + "/sso-connector.html";
|
this.stateService.setRememberedEmail(this.formGroup.value.email);
|
||||||
|
|
||||||
// Launch browser
|
this.router.navigate(["login"], { queryParams: { email: this.formGroup.value.email } });
|
||||||
this.platformUtilsService.launchUri(
|
}
|
||||||
url +
|
|
||||||
"/#/sso?clientId=browser" +
|
get selfHostedDomain() {
|
||||||
"&redirectUri=" +
|
return this.environmentService.hasBaseUrl() ? this.environmentService.getWebVaultUrl() : null;
|
||||||
encodeURIComponent(redirectUri) +
|
|
||||||
"&state=" +
|
|
||||||
state +
|
|
||||||
"&codeChallenge=" +
|
|
||||||
codeChallenge
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,12 @@
|
|||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
|
||||||
<header>
|
<header>
|
||||||
<div class="left">
|
<h1 class="login-center">
|
||||||
<button type="button" routerLink="/home">{{ "cancel" | i18n }}</button>
|
<span class="title">{{ "logIn" | i18n }}</span>
|
||||||
</div>
|
|
||||||
<h1 class="center">
|
|
||||||
<span class="title">{{ "appName" | i18n }}</span>
|
|
||||||
</h1>
|
</h1>
|
||||||
<div class="right">
|
|
||||||
<button type="submit" [disabled]="form.loading">
|
|
||||||
<span [hidden]="form.loading">{{ "login" | i18n }}</span>
|
|
||||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
<main tabindex="-1">
|
<main tabindex="-1">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<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>
|
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
@ -52,13 +39,27 @@
|
|||||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
<button type="button" class="btn link" routerLink="/hint" (click)="setFormValues()">
|
||||||
|
<b>{{ "getMasterPasswordHint" | i18n }}</b>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center text-muted" *ngIf="selfHostedDomain">
|
|
||||||
{{ "loggingInTo" | i18n: selfHostedDomain }}
|
|
||||||
</p>
|
|
||||||
<p class="text-center">
|
|
||||||
<button type="button" routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</button>
|
|
||||||
</p>
|
|
||||||
<app-private-mode-warning></app-private-mode-warning>
|
<app-private-mode-warning></app-private-mode-warning>
|
||||||
|
<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>
|
||||||
|
<button type="button" (click)="launchSsoBrowser()" class="btn block">
|
||||||
|
<i class="bwi bwi-bank" 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>
|
</main>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,27 +1,33 @@
|
|||||||
import { Component, NgZone } from "@angular/core";
|
import { Component, NgZone } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AppIdService } from "@bitwarden/common/abstractions/appId.service";
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
|
||||||
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-login",
|
selector: "app-login",
|
||||||
templateUrl: "login.component.html",
|
templateUrl: "login.component.html",
|
||||||
})
|
})
|
||||||
export class LoginComponent extends BaseLoginComponent {
|
export class LoginComponent extends BaseLoginComponent {
|
||||||
protected alwaysRememberEmail = true;
|
protected skipRememberEmail = true;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
apiService: ApiService,
|
||||||
|
appIdService: AppIdService,
|
||||||
authService: AuthService,
|
authService: AuthService,
|
||||||
router: Router,
|
router: Router,
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
@ -34,9 +40,13 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
logService: LogService,
|
logService: LogService,
|
||||||
ngZone: NgZone,
|
ngZone: NgZone,
|
||||||
formBuilder: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
formValidationErrorService: FormValidationErrorsService
|
formValidationErrorService: FormValidationErrorsService,
|
||||||
|
route: ActivatedRoute,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
apiService,
|
||||||
|
appIdService,
|
||||||
authService,
|
authService,
|
||||||
router,
|
router,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
@ -48,7 +58,9 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
logService,
|
logService,
|
||||||
ngZone,
|
ngZone,
|
||||||
formBuilder,
|
formBuilder,
|
||||||
formValidationErrorService
|
formValidationErrorService,
|
||||||
|
route,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = async () => {
|
super.onSuccessfulLogin = async () => {
|
||||||
await syncService.fullSync(true);
|
await syncService.fullSync(true);
|
||||||
@ -59,4 +71,45 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
settings() {
|
settings() {
|
||||||
this.router.navigate(["environment"]);
|
this.router.navigate(["environment"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async launchSsoBrowser() {
|
||||||
|
// 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.stateService.setSsoCodeVerifier(codeVerifier);
|
||||||
|
await this.stateService.setSsoState(state);
|
||||||
|
|
||||||
|
let url = this.environmentService.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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s
|
|||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
@ -44,7 +45,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
twoFactorService: TwoFactorService,
|
twoFactorService: TwoFactorService,
|
||||||
appIdService: AppIdService
|
appIdService: AppIdService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@ -58,9 +60,11 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
route,
|
route,
|
||||||
logService,
|
logService,
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
appIdService
|
appIdService,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
|
this.loginService.clearValues();
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
};
|
};
|
||||||
super.successRoute = "/tabs/vault";
|
super.successRoute = "/tabs/vault";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<app-callout type="warning" *ngIf="showWarning">
|
<app-callout class="app-private-mode-warning" type="warning" *ngIf="showWarning">
|
||||||
{{ "privateModeWarning" | i18n }}
|
{{ "privateModeWarning" | i18n }}
|
||||||
<a href="https://bitwarden.com/help/article/private-mode/" target="_blank" rel="noopener">{{
|
<a href="https://bitwarden.com/help/article/private-mode/" target="_blank" rel="noopener">{{
|
||||||
"learnMore" | i18n
|
"learnMore" | i18n
|
||||||
|
@ -174,6 +174,11 @@ header {
|
|||||||
|
|
||||||
.right {
|
.right {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
app-avatar {
|
||||||
|
max-height: 30px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
@ -183,6 +188,10 @@ header {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-center {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
app-pop-out > button,
|
app-pop-out > button,
|
||||||
div > button,
|
div > button,
|
||||||
div > a {
|
div > a {
|
||||||
|
@ -83,6 +83,11 @@
|
|||||||
margin: 5px 10px;
|
margin: 5px 10px;
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
|
|
||||||
|
button.btn {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@include themify($themes) {
|
@include themify($themes) {
|
||||||
color: themed("mutedColor");
|
color: themed("mutedColor");
|
||||||
}
|
}
|
||||||
|
@ -440,3 +440,7 @@ app-vault-view .box-footer {
|
|||||||
html.force_redraw {
|
html.force_redraw {
|
||||||
animation: redraw 1s linear infinite;
|
animation: redraw 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rounded-circle {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
@ -88,7 +88,7 @@ app-home {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app-private-mode-warning {
|
.app-private-mode-warning {
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
@ -115,3 +115,11 @@ body.body-full {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.createAccountLink {
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-buttons > button {
|
||||||
|
margin: 15px 0 15px 0;
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ import { FolderService } from "@bitwarden/common/abstractions/folder/folder.serv
|
|||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||||
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
|
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||||
@ -48,6 +49,7 @@ import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout
|
|||||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
|
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
|
||||||
import { AuthService } from "@bitwarden/common/services/auth.service";
|
import { AuthService } from "@bitwarden/common/services/auth.service";
|
||||||
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
|
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/services/login.service";
|
||||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||||
|
|
||||||
import MainBackground from "../../background/main.background";
|
import MainBackground from "../../background/main.background";
|
||||||
@ -309,6 +311,10 @@ function getBgService<T>(service: keyof MainBackground) {
|
|||||||
provide: FileDownloadService,
|
provide: FileDownloadService,
|
||||||
useClass: BrowserFileDownloadService,
|
useClass: BrowserFileDownloadService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoginServiceAbstraction,
|
||||||
|
useClass: LoginService,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: AbstractThemingService,
|
provide: AbstractThemingService,
|
||||||
useFactory: () => {
|
useFactory: () => {
|
||||||
|
@ -5,6 +5,7 @@ import { HintComponent as BaseHintComponent } from "@bitwarden/angular/component
|
|||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -17,8 +18,9 @@ export class HintComponent extends BaseHintComponent {
|
|||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
logService: LogService
|
logService: LogService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
super(router, i18nService, apiService, platformUtilsService, logService, loginService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,78 +22,135 @@
|
|||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<img class="logo-image" alt="Bitwarden" />
|
<img class="logo-image" alt="Bitwarden" />
|
||||||
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||||
<div class="box last">
|
<!-- start email -->
|
||||||
<div class="box-content">
|
<ng-container *ngIf="!validatedEmail; else loginPage">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box last">
|
||||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
<div class="box-content">
|
||||||
<input id="email" type="email" formControlName="email" appInputVerbatim="false" />
|
<div class="box-content-row" appBoxRow>
|
||||||
</div>
|
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
|
||||||
<div class="row-main">
|
|
||||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
|
||||||
<input
|
<input
|
||||||
id="masterPassword"
|
id="email"
|
||||||
type="{{ showPassword ? 'text' : 'password' }}"
|
type="email"
|
||||||
class="monospaced"
|
formControlName="email"
|
||||||
formControlName="masterPassword"
|
appInputVerbatim="false"
|
||||||
appInputVerbatim
|
(keyup.enter)="validateEmail()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
</div>
|
||||||
|
<div class="box-footer" *ngIf="selfHostedDomain">
|
||||||
|
{{ "loggingInTo" | i18n: selfHostedDomain }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox remember-email">
|
||||||
|
<label for="rememberEmail">
|
||||||
|
<input
|
||||||
|
id="rememberEmail"
|
||||||
|
type="checkbox"
|
||||||
|
name="rememberEmail"
|
||||||
|
formControlName="rememberEmail"
|
||||||
|
/>
|
||||||
|
{{ "rememberEmail" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="buttons with-rows">
|
||||||
|
<div class="buttons-row">
|
||||||
|
<button type="button" class="btn primary block" (click)="continue()">
|
||||||
|
{{ "continue" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-options">
|
||||||
|
<p class="no-margin">{{ "newAroundHere" | i18n }}</p>
|
||||||
|
<button type="button" class="text text-primary" routerLink="/register">
|
||||||
|
{{ "createAccount" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template [formGroup]="formGroup" #loginPage>
|
||||||
|
<div class="box last">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
|
<div class="row-main">
|
||||||
|
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="masterPassword"
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
class="monospaced"
|
||||||
|
formControlName="masterPassword"
|
||||||
|
appInputVerbatim
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="row-btn"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
[attr.aria-pressed]="showPassword"
|
||||||
|
(click)="togglePassword()"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="bwi bwi-lg"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box last" [hidden]="!showCaptcha()">
|
||||||
|
<div class="box-content">
|
||||||
|
<iframe id="hcaptcha_iframe" style="margin-top: 20px"></iframe>
|
||||||
|
<div class="box-content-row">
|
||||||
<button
|
<button
|
||||||
|
class="btn block"
|
||||||
type="button"
|
type="button"
|
||||||
class="row-btn"
|
routerLink="/accessibility-cookie"
|
||||||
appStopClick
|
(click)="setFormValues()"
|
||||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
|
||||||
[attr.aria-pressed]="showPassword"
|
|
||||||
(click)="togglePassword()"
|
|
||||||
>
|
>
|
||||||
<i
|
<i class="bwi bwi-universal-access" aria-hidden="true"></i>
|
||||||
class="bwi bwi-lg"
|
{{ "loadAccessibilityCookie" | i18n }}
|
||||||
aria-hidden="true"
|
|
||||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
|
||||||
></i>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="buttons with-rows">
|
||||||
<div class="box last" [hidden]="!showCaptcha()">
|
<div class="buttons-row">
|
||||||
<div class="box-content">
|
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||||
<iframe id="hcaptcha_iframe" style="margin-top: 20px"></iframe>
|
<b [hidden]="form.loading"
|
||||||
<div class="box-content-row">
|
><i class="bwi bwi-sign-in" aria-hidden="true"></i>
|
||||||
<button class="btn block" type="button" routerLink="/accessibility-cookie">
|
{{ "loginWithMasterPassword" | i18n }}</b
|
||||||
<i class="bwi bwi-universal-access" aria-hidden="true"></i>
|
>
|
||||||
{{ "loadAccessibilityCookie" | i18n }}
|
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons-row">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')"
|
||||||
|
class="btn block"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="sub-options">
|
||||||
<div class="buttons with-rows">
|
|
||||||
<div class="buttons-row">
|
|
||||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
|
||||||
<b [hidden]="form.loading"
|
|
||||||
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" routerLink="/register" class="btn block">
|
|
||||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="buttons-row">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
(click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')"
|
class="text text-primary password-hint-btn"
|
||||||
class="btn block"
|
routerLink="/hint"
|
||||||
|
(click)="setFormValues()"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
{{ "getMasterPasswordHint" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
<div>
|
||||||
|
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ loggedEmail }}</p>
|
||||||
|
<a [routerLink]="[]" (click)="toggleValidateEmail(false)">{{ "notYou" | i18n }}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ng-template>
|
||||||
<div class="sub-options">
|
|
||||||
<button type="button" routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AppIdService } from "@bitwarden/common/abstractions/appId.service";
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
||||||
@ -11,6 +13,7 @@ import { EnvironmentService } from "@bitwarden/common/abstractions/environment.s
|
|||||||
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
@ -29,13 +32,23 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
@ViewChild("environment", { read: ViewContainerRef, static: true })
|
@ViewChild("environment", { read: ViewContainerRef, static: true })
|
||||||
environmentModal: ViewContainerRef;
|
environmentModal: ViewContainerRef;
|
||||||
|
|
||||||
showingModal = false;
|
webVaultHostname = "";
|
||||||
|
|
||||||
protected alwaysRememberEmail = true;
|
showingModal = false;
|
||||||
|
|
||||||
private deferFocus: boolean = null;
|
private deferFocus: boolean = null;
|
||||||
|
|
||||||
|
get loggedEmail() {
|
||||||
|
return this.formGroup.value.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
get selfHostedDomain() {
|
||||||
|
return this.environmentService.hasBaseUrl() ? this.environmentService.getWebVaultUrl() : null;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
apiService: ApiService,
|
||||||
|
appIdService: AppIdService,
|
||||||
authService: AuthService,
|
authService: AuthService,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
@ -51,9 +64,13 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
formBuilder: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
formValidationErrorService: FormValidationErrorsService
|
formValidationErrorService: FormValidationErrorsService,
|
||||||
|
route: ActivatedRoute,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
apiService,
|
||||||
|
appIdService,
|
||||||
authService,
|
authService,
|
||||||
router,
|
router,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
@ -65,7 +82,9 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
logService,
|
logService,
|
||||||
ngZone,
|
ngZone,
|
||||||
formBuilder,
|
formBuilder,
|
||||||
formValidationErrorService
|
formValidationErrorService,
|
||||||
|
route,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
@ -127,7 +146,23 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
|||||||
this.showPassword = false;
|
this.showPassword = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async continue() {
|
||||||
|
await super.validateEmail();
|
||||||
|
if (!this.formGroup.controls.email.valid) {
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"error",
|
||||||
|
this.i18nService.t("errorOccured"),
|
||||||
|
this.i18nService.t("invalidEmail")
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
|
if (!this.validatedEmail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await super.submit();
|
await super.submit();
|
||||||
if (this.captchaSiteKey) {
|
if (this.captchaSiteKey) {
|
||||||
const content = document.getElementById("content") as HTMLDivElement;
|
const content = document.getElementById("content") as HTMLDivElement;
|
||||||
|
@ -9,6 +9,7 @@ import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
|||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
|
||||||
@ -41,7 +42,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
route: ActivatedRoute,
|
route: ActivatedRoute,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
twoFactorService: TwoFactorService,
|
twoFactorService: TwoFactorService,
|
||||||
appIdService: AppIdService
|
appIdService: AppIdService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@ -55,9 +57,11 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
route,
|
route,
|
||||||
logService,
|
logService,
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
appIdService
|
appIdService,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
super.onSuccessfulLogin = () => {
|
super.onSuccessfulLogin = () => {
|
||||||
|
this.loginService.clearValues();
|
||||||
return syncService.fullSync(true);
|
return syncService.fullSync(true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
<ng-container *ngIf="activeAccount?.email != null">
|
<ng-container *ngIf="activeAccount?.email != null">
|
||||||
<div class="border" *ngIf="numberOfAccounts > 0"></div>
|
<div class="border" *ngIf="numberOfAccounts > 0"></div>
|
||||||
<ng-container *ngIf="numberOfAccounts < 4">
|
<ng-container *ngIf="numberOfAccounts < 4">
|
||||||
<button type="button" class="add" routerLink="/login" (click)="addAccount()">
|
<button type="button" class="add" (click)="addAccount()">
|
||||||
<i class="bwi bwi-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
|
<i class="bwi bwi-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { animate, state, style, transition, trigger } from "@angular/animations";
|
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||||
import { ConnectedPosition } from "@angular/cdk/overlay";
|
import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
@ -91,6 +92,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
|||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
|
private router: Router,
|
||||||
private tokenService: TokenService
|
private tokenService: TokenService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -142,6 +144,8 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
|||||||
async addAccount() {
|
async addAccount() {
|
||||||
this.close();
|
this.close();
|
||||||
await this.stateService.setActiveUser(null);
|
await this.stateService.setActiveUser(null);
|
||||||
|
await this.stateService.setRememberedEmail(null);
|
||||||
|
this.router.navigate(["/login"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSwitcherAccounts(baseAccounts: {
|
private async createSwitcherAccounts(baseAccounts: {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
LogService,
|
LogService,
|
||||||
LogService as LogServiceAbstraction,
|
LogService as LogServiceAbstraction,
|
||||||
} from "@bitwarden/common/abstractions/log.service";
|
} from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||||
@ -35,6 +36,7 @@ import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abs
|
|||||||
import { ClientType } from "@bitwarden/common/enums/clientType";
|
import { ClientType } from "@bitwarden/common/enums/clientType";
|
||||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||||
import { GlobalState } from "@bitwarden/common/models/domain/global-state";
|
import { GlobalState } from "@bitwarden/common/models/domain/global-state";
|
||||||
|
import { LoginService } from "@bitwarden/common/services/login.service";
|
||||||
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
||||||
import { SystemService } from "@bitwarden/common/services/system.service";
|
import { SystemService } from "@bitwarden/common/services/system.service";
|
||||||
import { ElectronCryptoService } from "@bitwarden/electron/services/electronCrypto.service";
|
import { ElectronCryptoService } from "@bitwarden/electron/services/electronCrypto.service";
|
||||||
@ -175,6 +177,10 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
|||||||
EncryptedMessageHandlerService,
|
EncryptedMessageHandlerService,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoginServiceAbstraction,
|
||||||
|
useClass: LoginService,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ServicesModule {}
|
export class ServicesModule {}
|
||||||
|
@ -2021,5 +2021,32 @@
|
|||||||
},
|
},
|
||||||
"vault": {
|
"vault": {
|
||||||
"message": "Vault"
|
"message": "Vault"
|
||||||
|
},
|
||||||
|
"loginWithMasterPassword": {
|
||||||
|
"message": "Log in with master password"
|
||||||
|
},
|
||||||
|
"loggingInAs": {
|
||||||
|
"message": "Logging in as"
|
||||||
|
},
|
||||||
|
"rememberEmail": {
|
||||||
|
"message": "Remember email"
|
||||||
|
},
|
||||||
|
"notYou": {
|
||||||
|
"message": "Not you?"
|
||||||
|
},
|
||||||
|
"newAroundHere": {
|
||||||
|
"message": "New around here?"
|
||||||
|
},
|
||||||
|
"loggingInTo": {
|
||||||
|
"message": "Logging in to $DOMAIN$",
|
||||||
|
"placeholders": {
|
||||||
|
"domain": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logInWithAnotherDevice": {
|
||||||
|
"message": "Log in with another device"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,6 +310,11 @@ form,
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
margin-left: -18px;
|
margin-left: -18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.remember-email {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio {
|
.radio {
|
||||||
@ -482,6 +487,10 @@ app-root > #loading,
|
|||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-hint-btn {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.set-pin-modal {
|
.set-pin-modal {
|
||||||
.box {
|
.box {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
|
@ -189,6 +189,8 @@
|
|||||||
|
|
||||||
#login-page {
|
#login-page {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: unset;
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
@ -5,6 +5,7 @@ import { HintComponent as BaseHintComponent } from "@bitwarden/angular/component
|
|||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -17,8 +18,9 @@ export class HintComponent extends BaseHintComponent {
|
|||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
logService: LogService
|
logService: LogService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
super(router, i18nService, apiService, platformUtilsService, logService, loginService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,102 +16,122 @@
|
|||||||
<div
|
<div
|
||||||
class="tw-mt-3 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
|
class="tw-mt-3 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
|
||||||
>
|
>
|
||||||
<bit-callout
|
<ng-container *ngIf="!validatedEmail; else loginPage">
|
||||||
type="warning"
|
<div class="tw-mb-3">
|
||||||
title="{{ 'resetPasswordPolicyAutoEnroll' | i18n }}"
|
<bit-form-field>
|
||||||
*ngIf="showResetPasswordAutoEnrollWarning"
|
<bit-label>{{ "emailAddress" | i18n }}</bit-label>
|
||||||
>
|
<input
|
||||||
{{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
|
id="login_input_email"
|
||||||
</bit-callout>
|
bitInput
|
||||||
|
type="email"
|
||||||
<div class="tw-mb-3">
|
formControlName="email"
|
||||||
<bit-form-field>
|
(keyup.enter)="validateEmail()"
|
||||||
<bit-label>{{ "emailAddress" | i18n }}</bit-label>
|
/>
|
||||||
<input id="login_input_email" bitInput type="email" formControlName="email" />
|
</bit-form-field>
|
||||||
</bit-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tw-mb-3">
|
|
||||||
<bit-form-field>
|
|
||||||
<bit-label>{{ "masterPass" | i18n }}</bit-label>
|
|
||||||
<input
|
|
||||||
id="login_input_master-password"
|
|
||||||
bitInput
|
|
||||||
type="{{ showPassword ? 'text' : 'password' }}"
|
|
||||||
formControlName="masterPassword"
|
|
||||||
/>
|
|
||||||
<button type="button" bitSuffix bitButton (click)="togglePassword()">
|
|
||||||
<i
|
|
||||||
aria-hidden="true"
|
|
||||||
class="bwi bwi-lg bwi-eye"
|
|
||||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
<bit-hint>
|
|
||||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
|
||||||
</bit-hint>
|
|
||||||
</bit-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tw-mb-3 tw-flex tw-items-start">
|
|
||||||
<div class="tw-flex tw-h-6 tw-items-center">
|
|
||||||
<input
|
|
||||||
id="login_input_remember-email"
|
|
||||||
class="tw-w-4 tw-rounded tw-border"
|
|
||||||
bitInput
|
|
||||||
type="checkbox"
|
|
||||||
formControlName="rememberEmail"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<bit-label class="ml-2">
|
|
||||||
{{ "rememberEmail" | i18n }}
|
|
||||||
</bit-label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
<div class="tw-mb-3 tw-flex tw-items-start">
|
||||||
|
<div class="tw-flex tw-h-6 tw-items-center">
|
||||||
|
<input
|
||||||
|
id="login_input_remember-email"
|
||||||
|
class="tw-w-4 tw-rounded tw-border"
|
||||||
|
bitInput
|
||||||
|
type="checkbox"
|
||||||
|
formControlName="rememberEmail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<bit-label class="ml-2">
|
||||||
|
{{ "rememberEmail" | i18n }}
|
||||||
|
</bit-label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div [hidden]="!showCaptcha()">
|
<div class="tw-mb-3">
|
||||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
<button
|
||||||
</div>
|
bitButton
|
||||||
|
type="button"
|
||||||
|
buttonType="primary"
|
||||||
|
class="tw-w-full"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
(click)="validateEmail()"
|
||||||
|
>
|
||||||
|
<span> {{ "continue" | i18n }} </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tw-mb-3 tw-flex tw-space-x-4">
|
<hr />
|
||||||
<button
|
|
||||||
bitButton
|
|
||||||
buttonType="primary"
|
|
||||||
type="submit"
|
|
||||||
[block]="true"
|
|
||||||
[loading]="form.loading"
|
|
||||||
[disabled]="form.loading"
|
|
||||||
>
|
|
||||||
<span> <i class="bwi bwi-sign-in"></i> {{ "logIn" | i18n }} </span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a bitButton buttonType="secondary" routerLink="/register" [block]="true">
|
<p class="tw-m-0 tw-text-sm">
|
||||||
<i class="bwi bwi-pencil-square"></i>
|
{{ "newAroundHere" | i18n }}
|
||||||
{{ "createAccount" | i18n }}
|
<a routerLink="/register">{{ "createAccount" | i18n }}</a>
|
||||||
</a>
|
</p>
|
||||||
</div>
|
</ng-container>
|
||||||
|
|
||||||
<div class="tw-mb-3" *ngIf="!selfHosted && showPasswordless">
|
|
||||||
<button
|
|
||||||
bitButton
|
|
||||||
type="button"
|
|
||||||
buttonType="secondary"
|
|
||||||
class="tw-w-full"
|
|
||||||
(click)="startPasswordlessLogin()"
|
|
||||||
[disabled]="form.loading"
|
|
||||||
>
|
|
||||||
<span> <i class="bwi bwi-mobile"></i> {{ "loginWithDevice" | i18n }} </span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tw-mb-3">
|
|
||||||
<a routerLink="/sso" bitButton buttonType="secondary" class="tw-w-full">
|
|
||||||
<i class="bwi bwi-provider tw-mr-2"></i>
|
|
||||||
{{ "enterpriseSingleSignOn" | i18n }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ng-template [formGroup]="formGroup" #loginPage>
|
||||||
|
<div class="tw-mb-3">
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>{{ "masterPass" | i18n }}</bit-label>
|
||||||
|
<input
|
||||||
|
id="login_input_master-password"
|
||||||
|
bitInput
|
||||||
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
|
formControlName="masterPassword"
|
||||||
|
/>
|
||||||
|
<button type="button" bitSuffix bitButton (click)="togglePassword()">
|
||||||
|
<i
|
||||||
|
aria-hidden="true"
|
||||||
|
class="bwi bwi-lg bwi-eye"
|
||||||
|
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
<bit-hint>
|
||||||
|
<a routerLink="/hint" (click)="setFormValues()">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||||
|
</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div [hidden]="!showCaptcha()">
|
||||||
|
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-mb-3 tw-flex tw-space-x-4">
|
||||||
|
<button bitButton buttonType="primary" type="submit" [block]="true" [loading]="form.loading">
|
||||||
|
<span> {{ "loginWithMasterPassword" | i18n }} </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-mb-3" *ngIf="showLoginWithDevice && showPasswordless">
|
||||||
|
<button
|
||||||
|
bitButton
|
||||||
|
type="button"
|
||||||
|
[block]="true"
|
||||||
|
buttonType="secondary"
|
||||||
|
(click)="startPasswordlessLogin()"
|
||||||
|
>
|
||||||
|
<span> <i class="bwi bwi-mobile"></i> {{ "loginWithDevice" | i18n }} </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-mb-3">
|
||||||
|
<a
|
||||||
|
routerLink="/sso"
|
||||||
|
(click)="setFormValues()"
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
class="tw-w-full"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-provider tw-mr-2"></i>
|
||||||
|
{{ "enterpriseSingleSignOn" | i18n }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="tw-m-0 tw-text-sm">
|
||||||
|
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ loggedEmail }}</p>
|
||||||
|
<a [routerLink]="[]" (click)="toggleValidateEmail(false)">{{ "notYou" | i18n }}</a>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
@ -6,12 +6,14 @@ import { first } from "rxjs/operators";
|
|||||||
|
|
||||||
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AppIdService } from "@bitwarden/common/abstractions/appId.service";
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
@ -39,15 +41,16 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
apiService: ApiService,
|
||||||
|
appIdService: AppIdService,
|
||||||
authService: AuthService,
|
authService: AuthService,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
private route: ActivatedRoute,
|
route: ActivatedRoute,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
passwordGenerationService: PasswordGenerationService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
cryptoFunctionService: CryptoFunctionService,
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
private apiService: ApiService,
|
|
||||||
private policyApiService: PolicyApiServiceAbstraction,
|
private policyApiService: PolicyApiServiceAbstraction,
|
||||||
private policyService: InternalPolicyService,
|
private policyService: InternalPolicyService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
@ -56,9 +59,12 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private routerService: RouterService,
|
private routerService: RouterService,
|
||||||
formBuilder: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
formValidationErrorService: FormValidationErrorsService
|
formValidationErrorService: FormValidationErrorsService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
apiService,
|
||||||
|
appIdService,
|
||||||
authService,
|
authService,
|
||||||
router,
|
router,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
@ -70,7 +76,9 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
logService,
|
logService,
|
||||||
ngZone,
|
ngZone,
|
||||||
formBuilder,
|
formBuilder,
|
||||||
formValidationErrorService
|
formValidationErrorService,
|
||||||
|
route,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
this.onSuccessfulLogin = async () => {
|
this.onSuccessfulLogin = async () => {
|
||||||
this.messagingService.send("setFullWidth");
|
this.messagingService.send("setFullWidth");
|
||||||
@ -82,9 +90,6 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||||
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
|
|
||||||
this.formGroup.get("email")?.setValue(qParams.email);
|
|
||||||
}
|
|
||||||
if (qParams.premium != null) {
|
if (qParams.premium != null) {
|
||||||
this.routerService.setPreviousUrl("/settings/premium");
|
this.routerService.setPreviousUrl("/settings/premium");
|
||||||
} else if (qParams.org != null) {
|
} else if (qParams.org != null) {
|
||||||
@ -102,8 +107,6 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
this.routerService.setPreviousUrl(route.toString());
|
this.routerService.setPreviousUrl(route.toString());
|
||||||
}
|
}
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
const rememberEmail = await this.stateService.getRememberEmail();
|
|
||||||
this.formGroup.get("rememberEmail")?.setValue(rememberEmail);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const invite = await this.stateService.getOrganizationInvitation();
|
const invite = await this.stateService.getOrganizationInvitation();
|
||||||
@ -176,6 +179,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
|
|||||||
if (previousUrl) {
|
if (previousUrl) {
|
||||||
this.router.navigateByUrl(previousUrl);
|
this.router.navigateByUrl(previousUrl);
|
||||||
} else {
|
} else {
|
||||||
|
this.loginService.clearValues();
|
||||||
this.router.navigate([this.successRoute]);
|
this.router.navigate([this.successRoute]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
|||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
|
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||||
@ -40,7 +41,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
logService: LogService,
|
logService: LogService,
|
||||||
twoFactorService: TwoFactorService,
|
twoFactorService: TwoFactorService,
|
||||||
appIdService: AppIdService,
|
appIdService: AppIdService,
|
||||||
private routerService: RouterService
|
private routerService: RouterService,
|
||||||
|
loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@ -54,7 +56,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
route,
|
route,
|
||||||
logService,
|
logService,
|
||||||
twoFactorService,
|
twoFactorService,
|
||||||
appIdService
|
appIdService,
|
||||||
|
loginService
|
||||||
);
|
);
|
||||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||||
}
|
}
|
||||||
@ -79,6 +82,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async goAfterLogIn() {
|
async goAfterLogIn() {
|
||||||
|
this.loginService.clearValues();
|
||||||
const previousUrl = this.routerService.getPreviousUrl();
|
const previousUrl = this.routerService.getPreviousUrl();
|
||||||
if (previousUrl) {
|
if (previousUrl) {
|
||||||
this.router.navigateByUrl(previousUrl);
|
this.router.navigateByUrl(previousUrl);
|
||||||
|
@ -13,6 +13,7 @@ import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.
|
|||||||
import { ModalService as ModalServiceAbstraction } from "@bitwarden/angular/services/modal.service";
|
import { ModalService as ModalServiceAbstraction } from "@bitwarden/angular/services/modal.service";
|
||||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
@ -20,6 +21,7 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/a
|
|||||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/abstractions/stateMigration.service";
|
import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/abstractions/stateMigration.service";
|
||||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||||
|
import { LoginService } from "@bitwarden/common/services/login.service";
|
||||||
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
||||||
|
|
||||||
import { BroadcasterMessagingService } from "./broadcaster-messaging.service";
|
import { BroadcasterMessagingService } from "./broadcaster-messaging.service";
|
||||||
@ -98,6 +100,10 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
|||||||
provide: FileDownloadService,
|
provide: FileDownloadService,
|
||||||
useClass: WebFileDownloadService,
|
useClass: WebFileDownloadService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoginServiceAbstraction,
|
||||||
|
useClass: LoginService,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreModule {
|
export class CoreModule {
|
||||||
|
@ -569,12 +569,15 @@
|
|||||||
"loginOrCreateNewAccount": {
|
"loginOrCreateNewAccount": {
|
||||||
"message": "Log in or create a new account to access your secure vault."
|
"message": "Log in or create a new account to access your secure vault."
|
||||||
},
|
},
|
||||||
"loginWithDevice" : {
|
"loginWithDevice": {
|
||||||
"message": "Log in with device"
|
"message": "Log in with device"
|
||||||
},
|
},
|
||||||
"loginWithDeviceEnabledInfo": {
|
"loginWithDeviceEnabledInfo": {
|
||||||
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?"
|
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?"
|
||||||
},
|
},
|
||||||
|
"loginWithMasterPassword": {
|
||||||
|
"message": "Log in with master password"
|
||||||
|
},
|
||||||
"createAccount": {
|
"createAccount": {
|
||||||
"message": "Create account"
|
"message": "Create account"
|
||||||
},
|
},
|
||||||
@ -717,7 +720,7 @@
|
|||||||
"noOrganizationsList": {
|
"noOrganizationsList": {
|
||||||
"message": "You do not belong to any organizations. Organizations allow you to securely share items with other users."
|
"message": "You do not belong to any organizations. Organizations allow you to securely share items with other users."
|
||||||
},
|
},
|
||||||
"notificationSentDevice":{
|
"notificationSentDevice": {
|
||||||
"message": "A notification has been sent to your device."
|
"message": "A notification has been sent to your device."
|
||||||
},
|
},
|
||||||
"versionNumber": {
|
"versionNumber": {
|
||||||
@ -5394,6 +5397,12 @@
|
|||||||
"numberOfUsers": {
|
"numberOfUsers": {
|
||||||
"message": "Number of users"
|
"message": "Number of users"
|
||||||
},
|
},
|
||||||
|
"loggingInAs": {
|
||||||
|
"message": "Logging in as"
|
||||||
|
},
|
||||||
|
"notYou": {
|
||||||
|
"message": "Not you?"
|
||||||
|
},
|
||||||
"multiSelectPlaceholder": {
|
"multiSelectPlaceholder": {
|
||||||
"message": "-- Type to Filter --"
|
"message": "-- Type to Filter --"
|
||||||
},
|
},
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
import { Directive, OnInit } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { PasswordHintRequest } from "@bitwarden/common/models/request/password-hint.request";
|
import { PasswordHintRequest } from "@bitwarden/common/models/request/password-hint.request";
|
||||||
|
|
||||||
export class HintComponent {
|
@Directive()
|
||||||
|
export class HintComponent implements OnInit {
|
||||||
email = "";
|
email = "";
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
|
|
||||||
@ -18,9 +21,14 @@ export class HintComponent {
|
|||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected apiService: ApiService,
|
protected apiService: ApiService,
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
private logService: LogService
|
private logService: LogService,
|
||||||
|
private loginService: LoginService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.email = this.loginService.getEmail() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
if (this.email == null || this.email === "") {
|
if (this.email == null || this.email === "") {
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Directive, NgZone, OnInit } from "@angular/core";
|
import { Directive, NgZone, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { take } from "rxjs/operators";
|
import { take } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AppIdService } from "@bitwarden/common/abstractions/appId.service";
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
@ -12,6 +14,7 @@ import {
|
|||||||
} from "@bitwarden/common/abstractions/formValidationErrors.service";
|
} from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
@ -29,20 +32,30 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
onSuccessfulLoginNavigate: () => Promise<any>;
|
onSuccessfulLoginNavigate: () => Promise<any>;
|
||||||
onSuccessfulLoginTwoFactorNavigate: () => Promise<any>;
|
onSuccessfulLoginTwoFactorNavigate: () => Promise<any>;
|
||||||
onSuccessfulLoginForceResetNavigate: () => Promise<any>;
|
onSuccessfulLoginForceResetNavigate: () => Promise<any>;
|
||||||
selfHosted = false;
|
private selfHosted = false;
|
||||||
|
showLoginWithDevice: boolean;
|
||||||
|
validatedEmail = false;
|
||||||
|
paramEmailSet = false;
|
||||||
|
|
||||||
formGroup = this.formBuilder.group({
|
formGroup = this.formBuilder.group({
|
||||||
email: ["", [Validators.required, Validators.email]],
|
email: ["", [Validators.required, Validators.email]],
|
||||||
masterPassword: ["", [Validators.required, Validators.minLength(8)]],
|
masterPassword: ["", [Validators.required, Validators.minLength(8)]],
|
||||||
rememberEmail: [true],
|
rememberEmail: [false],
|
||||||
});
|
});
|
||||||
|
|
||||||
protected twoFactorRoute = "2fa";
|
protected twoFactorRoute = "2fa";
|
||||||
protected successRoute = "vault";
|
protected successRoute = "vault";
|
||||||
protected forcePasswordResetRoute = "update-temp-password";
|
protected forcePasswordResetRoute = "update-temp-password";
|
||||||
protected alwaysRememberEmail = false;
|
protected alwaysRememberEmail = false;
|
||||||
|
protected skipRememberEmail = false;
|
||||||
|
|
||||||
|
get loggedEmail() {
|
||||||
|
return this.formGroup.value.email;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
protected apiService: ApiService,
|
||||||
|
protected appIdService: AppIdService,
|
||||||
protected authService: AuthService,
|
protected authService: AuthService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
@ -54,7 +67,9 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
protected logService: LogService,
|
protected logService: LogService,
|
||||||
protected ngZone: NgZone,
|
protected ngZone: NgZone,
|
||||||
protected formBuilder: FormBuilder,
|
protected formBuilder: FormBuilder,
|
||||||
protected formValidationErrorService: FormValidationErrorsService
|
protected formValidationErrorService: FormValidationErrorsService,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(environmentService, i18nService, platformUtilsService);
|
super(environmentService, i18nService, platformUtilsService);
|
||||||
this.selfHosted = platformUtilsService.isSelfHost();
|
this.selfHosted = platformUtilsService.isSelfHost();
|
||||||
@ -65,19 +80,35 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
let email = this.formGroup.value.email;
|
this.route?.queryParams.subscribe((params) => {
|
||||||
|
if (params != null) {
|
||||||
|
const queryParamsEmail = params["email"];
|
||||||
|
if (queryParamsEmail != null && queryParamsEmail.indexOf("@") > -1) {
|
||||||
|
this.formGroup.get("email").setValue(queryParamsEmail);
|
||||||
|
this.paramEmailSet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let email = this.loginService.getEmail();
|
||||||
|
|
||||||
if (email == null || email === "") {
|
if (email == null || email === "") {
|
||||||
email = await this.stateService.getRememberedEmail();
|
email = await this.stateService.getRememberedEmail();
|
||||||
this.formGroup.get("email")?.setValue(email);
|
}
|
||||||
|
|
||||||
if (email == null) {
|
if (!this.paramEmailSet) {
|
||||||
this.formGroup.get("email")?.setValue("");
|
this.formGroup.get("email")?.setValue(email ?? "");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!this.alwaysRememberEmail) {
|
if (!this.alwaysRememberEmail) {
|
||||||
const rememberEmail = (await this.stateService.getRememberedEmail()) != null;
|
let rememberEmail = this.loginService.getRememberEmail();
|
||||||
|
if (rememberEmail == null) {
|
||||||
|
rememberEmail = (await this.stateService.getRememberedEmail()) != null;
|
||||||
|
}
|
||||||
this.formGroup.get("rememberEmail")?.setValue(rememberEmail);
|
this.formGroup.get("rememberEmail")?.setValue(rememberEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
this.validateEmail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit(showToast = true) {
|
async submit(showToast = true) {
|
||||||
@ -108,6 +139,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
);
|
);
|
||||||
this.formPromise = this.authService.logIn(credentials);
|
this.formPromise = this.authService.logIn(credentials);
|
||||||
const response = await this.formPromise;
|
const response = await this.formPromise;
|
||||||
|
this.setFormValues();
|
||||||
if (data.rememberEmail || this.alwaysRememberEmail) {
|
if (data.rememberEmail || this.alwaysRememberEmail) {
|
||||||
await this.stateService.setRememberedEmail(data.email);
|
await this.stateService.setRememberedEmail(data.email);
|
||||||
} else {
|
} else {
|
||||||
@ -130,6 +162,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
} else {
|
} else {
|
||||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||||
await this.stateService.setDisableFavicon(!!disableFavicon);
|
await this.stateService.setDisableFavicon(!!disableFavicon);
|
||||||
|
this.loginService.clearValues();
|
||||||
if (this.onSuccessfulLogin != null) {
|
if (this.onSuccessfulLogin != null) {
|
||||||
this.onSuccessfulLogin();
|
this.onSuccessfulLogin();
|
||||||
}
|
}
|
||||||
@ -191,6 +224,25 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async validateEmail() {
|
||||||
|
this.formGroup.controls.email.markAsTouched();
|
||||||
|
const emailInvalid = this.formGroup.get("email").invalid;
|
||||||
|
if (!emailInvalid) {
|
||||||
|
this.toggleValidateEmail(true);
|
||||||
|
await this.getLoginWithDevice(this.loggedEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleValidateEmail(value: boolean) {
|
||||||
|
this.validatedEmail = value;
|
||||||
|
this.formGroup.controls.masterPassword.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormValues() {
|
||||||
|
this.loginService.setEmail(this.formGroup.value.email);
|
||||||
|
this.loginService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||||
|
}
|
||||||
|
|
||||||
private getErrorToastMessage() {
|
private getErrorToastMessage() {
|
||||||
const error: AllValidationErrors = this.formValidationErrorService
|
const error: AllValidationErrors = this.formValidationErrorService
|
||||||
.getFormValidationErrors(this.formGroup.controls)
|
.getFormValidationErrors(this.formGroup.controls)
|
||||||
@ -213,8 +265,19 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
|||||||
return `${error.controlName}${name}`;
|
return `${error.controlName}${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getLoginWithDevice(email: string) {
|
||||||
|
try {
|
||||||
|
const deviceIdentifier = await this.appIdService.getAppId();
|
||||||
|
const res = await this.apiService.getKnownDevice(email, deviceIdentifier);
|
||||||
|
//ensure the application is not self-hosted
|
||||||
|
this.showLoginWithDevice = res && !this.selfHosted;
|
||||||
|
} catch (e) {
|
||||||
|
this.showLoginWithDevice = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected focusInput() {
|
protected focusInput() {
|
||||||
const email = this.formGroup.value.email;
|
const email = this.loggedEmail;
|
||||||
document.getElementById(email == null || email === "" ? "email" : "masterPassword").focus();
|
document.getElementById(email == null || email === "" ? "email" : "masterPassword").focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
|||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
|
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||||
@ -59,7 +60,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
protected logService: LogService,
|
protected logService: LogService,
|
||||||
protected twoFactorService: TwoFactorService,
|
protected twoFactorService: TwoFactorService,
|
||||||
protected appIdService: AppIdService
|
protected appIdService: AppIdService,
|
||||||
|
protected loginService: LoginService
|
||||||
) {
|
) {
|
||||||
super(environmentService, i18nService, platformUtilsService);
|
super(environmentService, i18nService, platformUtilsService);
|
||||||
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
||||||
@ -204,6 +206,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.onSuccessfulLogin != null) {
|
if (this.onSuccessfulLogin != null) {
|
||||||
|
this.loginService.clearValues();
|
||||||
this.onSuccessfulLogin();
|
this.onSuccessfulLogin();
|
||||||
}
|
}
|
||||||
if (response.resetMasterPassword) {
|
if (response.resetMasterPassword) {
|
||||||
@ -213,8 +216,10 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
this.successRoute = "update-temp-password";
|
this.successRoute = "update-temp-password";
|
||||||
}
|
}
|
||||||
if (this.onSuccessfulLoginNavigate != null) {
|
if (this.onSuccessfulLoginNavigate != null) {
|
||||||
|
this.loginService.clearValues();
|
||||||
this.onSuccessfulLoginNavigate();
|
this.onSuccessfulLoginNavigate();
|
||||||
} else {
|
} else {
|
||||||
|
this.loginService.clearValues();
|
||||||
this.router.navigate([this.successRoute], {
|
this.router.navigate([this.successRoute], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
identifier: this.identifier,
|
identifier: this.identifier,
|
||||||
|
@ -31,6 +31,7 @@ import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction }
|
|||||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/abstractions/keyConnector.service";
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||||
|
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/abstractions/login.service";
|
||||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
||||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
||||||
@ -88,6 +89,7 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s
|
|||||||
import { FolderService } from "@bitwarden/common/services/folder/folder.service";
|
import { FolderService } from "@bitwarden/common/services/folder/folder.service";
|
||||||
import { FormValidationErrorsService } from "@bitwarden/common/services/formValidationErrors.service";
|
import { FormValidationErrorsService } from "@bitwarden/common/services/formValidationErrors.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
|
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
|
||||||
|
import { LoginService } from "@bitwarden/common/services/login.service";
|
||||||
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
||||||
import { OrganizationApiService } from "@bitwarden/common/services/organization/organization-api.service";
|
import { OrganizationApiService } from "@bitwarden/common/services/organization/organization-api.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
|
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
|
||||||
@ -578,6 +580,10 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
|
|||||||
useClass: ValidationService,
|
useClass: ValidationService,
|
||||||
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoginServiceAbstraction,
|
||||||
|
useClass: LoginService,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class JslibServicesModule {}
|
export class JslibServicesModule {}
|
||||||
|
@ -479,6 +479,7 @@ export abstract class ApiService {
|
|||||||
putDeviceVerificationSettings: (
|
putDeviceVerificationSettings: (
|
||||||
request: DeviceVerificationRequest
|
request: DeviceVerificationRequest
|
||||||
) => Promise<DeviceVerificationResponse>;
|
) => Promise<DeviceVerificationResponse>;
|
||||||
|
getKnownDevice: (email: string, deviceIdentifier: string) => Promise<boolean>;
|
||||||
|
|
||||||
getEmergencyAccessTrusted: () => Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>>;
|
getEmergencyAccessTrusted: () => Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>>;
|
||||||
getEmergencyAccessGranted: () => Promise<ListResponse<EmergencyAccessGrantorDetailsResponse>>;
|
getEmergencyAccessGranted: () => Promise<ListResponse<EmergencyAccessGrantorDetailsResponse>>;
|
||||||
|
7
libs/common/src/abstractions/login.service.ts
Normal file
7
libs/common/src/abstractions/login.service.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export abstract class LoginService {
|
||||||
|
getEmail: () => string;
|
||||||
|
getRememberEmail: () => boolean;
|
||||||
|
setEmail: (value: string) => void;
|
||||||
|
setRememberEmail: (value: boolean) => void;
|
||||||
|
clearValues: () => void;
|
||||||
|
}
|
@ -1518,6 +1518,12 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return new DeviceVerificationResponse(r);
|
return new DeviceVerificationResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getKnownDevice(email: string, deviceIdentifier: string): Promise<boolean> {
|
||||||
|
const path = `/devices/knowndevice/${email}/${deviceIdentifier}`;
|
||||||
|
const r = await this.send("GET", path, null, false, true);
|
||||||
|
return r as boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// Emergency Access APIs
|
// Emergency Access APIs
|
||||||
|
|
||||||
async getEmergencyAccessTrusted(): Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>> {
|
async getEmergencyAccessTrusted(): Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>> {
|
||||||
|
27
libs/common/src/services/login.service.ts
Normal file
27
libs/common/src/services/login.service.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { LoginService as LoginServiceAbstraction } from "../abstractions/login.service";
|
||||||
|
|
||||||
|
export class LoginService implements LoginServiceAbstraction {
|
||||||
|
private _email: string;
|
||||||
|
private _rememberEmail: boolean;
|
||||||
|
|
||||||
|
getEmail() {
|
||||||
|
return this._email;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRememberEmail() {
|
||||||
|
return this._rememberEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEmail(value: string) {
|
||||||
|
this._email = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRememberEmail(value: boolean) {
|
||||||
|
this._rememberEmail = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearValues() {
|
||||||
|
this._email = null;
|
||||||
|
this._rememberEmail = null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user