diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index fea9c04ca6..41a8173487 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2046,5 +2046,35 @@ }, "rememberEmail": { "message": "Remember email" + }, + "exposedMasterPassword": { + "message": "Exposed Master Password" + }, + "exposedMasterPasswordDesc": { + "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + }, + "weakAndExposedMasterPassword": { + "message": "Weak and Exposed Master Password" + }, + "weakAndBreachedMasterPasswordDesc": { + "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + }, + "checkForBreaches": { + "message": "Check known data breaches for this password" + }, + "important": { + "message": "Important:" + }, + "masterPasswordHint": { + "message": "Your master password cannot be recovered if you forget it!" + }, + "characterMinimum": { + "message": "$LENGTH$ character minimum", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } } } diff --git a/apps/browser/src/popup/accounts/register.component.html b/apps/browser/src/popup/accounts/register.component.html index 6f7e543ea1..6386571e60 100644 --- a/apps/browser/src/popup/accounts/register.component.html +++ b/apps/browser/src/popup/accounts/register.component.html @@ -66,7 +66,8 @@
@@ -107,6 +108,17 @@ +
+
+ + +
+
diff --git a/apps/browser/src/popup/accounts/register.component.ts b/apps/browser/src/popup/accounts/register.component.ts index 6ee4719457..d199b48485 100644 --- a/apps/browser/src/popup/accounts/register.component.ts +++ b/apps/browser/src/popup/accounts/register.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; @@ -34,7 +35,8 @@ export class RegisterComponent extends BaseRegisterComponent { platformUtilsService: PlatformUtilsService, passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -48,7 +50,8 @@ export class RegisterComponent extends BaseRegisterComponent { platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } } diff --git a/apps/browser/src/popup/scss/box.scss b/apps/browser/src/popup/scss/box.scss index a14427357f..ef13a7f545 100644 --- a/apps/browser/src/popup/scss/box.scss +++ b/apps/browser/src/popup/scss/box.scss @@ -83,6 +83,10 @@ padding-bottom: 10px; margin: 5px; } + + &.row-top-padding { + padding-top: 10px; + } } .box-footer { diff --git a/apps/desktop/src/app/accounts/register.component.ts b/apps/desktop/src/app/accounts/register.component.ts index 2cfc1cd8ad..94706064d2 100644 --- a/apps/desktop/src/app/accounts/register.component.ts +++ b/apps/desktop/src/app/accounts/register.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; @@ -36,7 +37,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit, environmentService: EnvironmentService, private broadcasterService: BroadcasterService, private ngZone: NgZone, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -50,7 +52,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit, platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } diff --git a/apps/web/src/app/accounts/register-form/register-form.component.ts b/apps/web/src/app/accounts/register-form/register-form.component.ts index 6cd62ea13e..242ce39baf 100644 --- a/apps/web/src/app/accounts/register-form/register-form.component.ts +++ b/apps/web/src/app/accounts/register-form/register-form.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; @@ -41,7 +42,8 @@ export class RegisterFormComponent extends BaseRegisterComponent { passwordGenerationService: PasswordGenerationService, private policyService: PolicyService, environmentService: EnvironmentService, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -55,7 +57,8 @@ export class RegisterFormComponent extends BaseRegisterComponent { platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } diff --git a/libs/angular/src/components/register.component.ts b/libs/angular/src/components/register.component.ts index 029639e7eb..c370bea8b3 100644 --- a/libs/angular/src/components/register.component.ts +++ b/libs/angular/src/components/register.component.ts @@ -3,6 +3,7 @@ import { AbstractControl, UntypedFormBuilder, ValidatorFn, Validators } from "@a import { Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; @@ -38,6 +39,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn showTerms = true; showErrorSummary = false; passwordStrengthResult: any; + characterMinimumMessage: string; + minimumLength = 8; color: string; text: string; @@ -45,8 +48,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn { email: ["", [Validators.required, Validators.email]], name: [""], - masterPassword: ["", [Validators.required, Validators.minLength(8)]], - confirmMasterPassword: ["", [Validators.required, Validators.minLength(8)]], + masterPassword: ["", [Validators.required, Validators.minLength(this.minimumLength)]], + confirmMasterPassword: ["", [Validators.required, Validators.minLength(this.minimumLength)]], hint: [ null, [ @@ -56,6 +59,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn ), ], ], + checkForBreaches: [false], acceptPolicies: [false, [this.acceptPoliciesValidation()]], }, { @@ -85,10 +89,12 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn platformUtilsService: PlatformUtilsService, protected passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService, - protected logService: LogService + protected logService: LogService, + protected auditService: AuditService ) { super(environmentService, i18nService, platformUtilsService); this.showTerms = !platformUtilsService.isSelfHost(); + this.characterMinimumMessage = this.i18nService.t("characterMinimum", this.minimumLength); } async ngOnInit() { @@ -212,7 +218,24 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn return { isValid: false }; } - if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) { + const passwordWeak = + this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3; + const passwordLeak = + this.formGroup.controls.checkForBreaches.value && + (await this.auditService.passwordLeaked(this.formGroup.controls.masterPassword.value)) > 0; + + if (passwordWeak && passwordLeak) { + const result = await this.platformUtilsService.showDialog( + this.i18nService.t("weakAndBreachedMasterPasswordDesc"), + this.i18nService.t("weakAndExposedMasterPassword"), + this.i18nService.t("yes"), + this.i18nService.t("no"), + "warning" + ); + if (!result) { + return { isValid: false }; + } + } else if (passwordWeak) { const result = await this.platformUtilsService.showDialog( this.i18nService.t("weakMasterPasswordDesc"), this.i18nService.t("weakMasterPassword"), @@ -223,7 +246,19 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn if (!result) { return { isValid: false }; } + } else if (passwordLeak) { + const result = await this.platformUtilsService.showDialog( + this.i18nService.t("exposedMasterPasswordDesc"), + this.i18nService.t("exposedMasterPassword"), + this.i18nService.t("yes"), + this.i18nService.t("no"), + "warning" + ); + if (!result) { + return { isValid: false }; + } } + return { isValid: true }; }