1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-27 12:36:14 +01:00

call api to set password with key parameters (#609)

* call api to set password with key parameters

* update ssoCompleteRegistration string
This commit is contained in:
Kyle Spearrin 2020-08-17 15:04:59 -04:00 committed by GitHub
parent 1fe7554818
commit e0ede7ba74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 134 additions and 57 deletions

2
jslib

@ -1 +1 @@
Subproject commit ed6978baff5b129341bd46cc90a6155c1bcc5124 Subproject commit 7bf00b4fb37d85d8ffe7aa3248bd72f304297331

View File

@ -1,7 +1,11 @@
<div class="secondary-header"> <form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
<h1>{{'setMasterPassword' | i18n}}</h1> <div class="row justify-content-md-center mt-5">
</div> <div class="col-5">
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout> <p class="lead text-center mb-4">{{'setMasterPassword' | i18n}}</p>
<div class="card d-block">
<div class="card-body">
<app-callout type="info">{{'ssoCompleteRegistration' | i18n}}</app-callout>
<div class="form-group">
<app-callout type="info" *ngIf="enforcedPolicyOptions"> <app-callout type="info" *ngIf="enforcedPolicyOptions">
{{'masterPasswordPolicyInEffect' | i18n}} {{'masterPasswordPolicyInEffect' | i18n}}
<ul class="mb-0"> <ul class="mb-0">
@ -11,35 +15,67 @@
<li *ngIf="enforcedPolicyOptions?.minLength > 0"> <li *ngIf="enforcedPolicyOptions?.minLength > 0">
{{'policyInEffectMinLength' | i18n : enforcedPolicyOptions?.minLength.toString()}} {{'policyInEffectMinLength' | i18n : enforcedPolicyOptions?.minLength.toString()}}
</li> </li>
<li *ngIf="enforcedPolicyOptions?.requireUpper">{{'policyInEffectUppercase' | i18n}}</li> <li *ngIf="enforcedPolicyOptions?.requireUpper">
<li *ngIf="enforcedPolicyOptions?.requireLower">{{'policyInEffectLowercase' | i18n}}</li> {{'policyInEffectUppercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireNumbers">{{'policyInEffectNumbers' | i18n}}</li> <li *ngIf="enforcedPolicyOptions?.requireLower">
<li *ngIf="enforcedPolicyOptions?.requireSpecial">{{'policyInEffectSpecial' | i18n : '!@#$%^&*'}}</li> {{'policyInEffectLowercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireNumbers">
{{'policyInEffectNumbers' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireSpecial">
{{'policyInEffectSpecial' | i18n : '!@#$%^&*'}}</li>
</ul> </ul>
</app-callout> </app-callout>
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off"> <div class="d-flex">
<div class="row"> <div class="w-100">
<div class="col-6"> <input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
<div class="form-group"> name="MasterPasswordHash" class="text-monospace form-control mb-1"
<label for="newMasterPassword">{{'newMasterPass' | i18n}}</label> [(ngModel)]="masterPassword" (input)="updatePasswordStrength()" required
<input id="newMasterPassword" type="password" name="NewMasterPasswordHash" class="form-control mb-1" appInputVerbatim>
[(ngModel)]="newMasterPassword" (input)="updatePasswordStrength()" required appInputVerbatim <app-password-strength [score]="masterPasswordScore" [showText]="true">
autocomplete="new-password"> </app-password-strength>
<app-password-strength [score]="masterPasswordScore" [showText]="true"></app-password-strength>
</div> </div>
</div> <div>
<div class="col-6"> <button type="button" class="ml-1 btn btn-link"
<div class="form-group"> appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
<label for="confirmNewMasterPassword">{{'confirmNewMasterPass' | i18n}}</label> <i class="fa fa-lg" aria-hidden="true"
<input id="confirmNewMasterPassword" type="password" name="ConfirmNewMasterPasswordHash" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
class="form-control" [(ngModel)]="confirmNewMasterPassword" required appInputVerbatim
autocomplete="new-password">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'setMasterPassword' | i18n}}</span>
</button> </button>
<div class="progress-bar invisible"></div>
</div>
</div>
<small class="form-text text-muted">{{'masterPassDesc' | i18n}}</small>
</div>
<div class="form-group">
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
<div class="d-flex">
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
name="MasterPasswordRetype" class="text-monospace form-control"
[(ngModel)]="masterPasswordRetype" required appInputVerbatim>
<button type="button" class="ml-1 btn btn-link" appA11yTitle="{{'toggleVisibility' | i18n}}"
(click)="togglePassword(true)">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</button>
</div>
</div>
<div class="form-group">
<label for="hint">{{'masterPassHint' | i18n}}</label>
<input id="hint" class="form-control" type="text" name="Hint" [(ngModel)]="hint">
<small class="form-text text-muted">{{'masterPassHintDesc' | i18n}}</small>
</div>
<hr>
<div class="d-flex">
<button type="submit" class="btn btn-primary btn-block btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span>{{'submit' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary btn-block ml-2 mt-0" (click)="logOut()">
{{'logOut' | i18n}}
</button>
</div>
</div>
</div>
</div>
</div>
</form> </form>

View File

@ -19,19 +19,25 @@ import { UserService } from 'jslib/abstractions/user.service';
import { CipherString } from 'jslib/models/domain/cipherString'; import { CipherString } from 'jslib/models/domain/cipherString';
import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey'; import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey';
import { KeysRequest } from 'jslib/models/request/keysRequest';
import { SetPasswordRequest } from 'jslib/models/request/setPasswordRequest'; import { SetPasswordRequest } from 'jslib/models/request/setPasswordRequest';
import { import {
ChangePasswordComponent as BaseChangePasswordComponent, ChangePasswordComponent as BaseChangePasswordComponent,
} from 'jslib/angular/components/change-password.component'; } from 'jslib/angular/components/change-password.component';
import { KdfType } from 'jslib/enums/kdfType';
@Component({ @Component({
selector: 'app-accounts-change-password', selector: 'app-accounts-change-password',
templateUrl: 'change-password.component.html', templateUrl: 'change-password.component.html',
}) })
export class ChangePasswordComponent extends BaseChangePasswordComponent { export class ChangePasswordComponent extends BaseChangePasswordComponent {
showPassword: boolean = false;
hint: string = '';
onSuccessfulChangePassword: () => Promise<any>; onSuccessfulChangePassword: () => Promise<any>;
successRoute = 'lock'; successRoute = 'vault';
constructor(apiService: ApiService, i18nService: I18nService, constructor(apiService: ApiService, i18nService: I18nService,
cryptoService: CryptoService, messagingService: MessagingService, cryptoService: CryptoService, messagingService: MessagingService,
@ -43,16 +49,36 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
platformUtilsService, folderService, cipherService, syncService, policyService, router); platformUtilsService, folderService, cipherService, syncService, policyService, router);
} }
async performSubmitActions(newMasterPasswordHash: string, newKey: SymmetricCryptoKey, async setupSubmitActions() {
newEncKey: [SymmetricCryptoKey, CipherString]) { this.kdf = KdfType.PBKDF2_SHA256;
const setRequest = new SetPasswordRequest(); const useLowerKdf = this.platformUtilsService.isEdge() || this.platformUtilsService.isIE();
setRequest.newMasterPasswordHash = newMasterPasswordHash; this.kdfIterations = useLowerKdf ? 10000 : 100000;
setRequest.key = newEncKey[1].encryptedString; return true;
}
async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey,
encKey: [SymmetricCryptoKey, CipherString]) {
const request = new SetPasswordRequest();
request.masterPasswordHash = masterPasswordHash;
request.key = encKey[1].encryptedString;
request.masterPasswordHint = this.hint;
request.kdf = this.kdf;
request.kdfIterations = this.kdfIterations;
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
try { try {
this.formPromise = this.apiService.setPassword(setRequest); this.formPromise = this.apiService.setPassword(request);
await this.formPromise; await this.formPromise;
await this.userService.setInformation(await this.userService.getUserId(), await this.userService.getEmail(),
this.kdf, this.kdfIterations);
await this.cryptoService.setKey(key);
await this.cryptoService.setKeyHash(masterPasswordHash);
await this.cryptoService.setEncKey(encKey[1].encryptedString);
await this.cryptoService.setEncPrivateKey(keys[1].encryptedString);
if (this.onSuccessfulChangePassword != null) { if (this.onSuccessfulChangePassword != null) {
this.onSuccessfulChangePassword(); this.onSuccessfulChangePassword();
} else { } else {
@ -62,4 +88,10 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred')); this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred'));
} }
} }
togglePassword(confirmField: boolean) {
this.platformUtilsService.eventTrack('Toggled Master Password on Set Password');
this.showPassword = !this.showPassword;
document.getElementById(confirmField ? 'masterPasswordRetype' : 'masterPassword').focus();
}
} }

View File

@ -108,7 +108,6 @@ const routes: Routes = [
}, },
{ {
path: 'change-password', component: ChangePasswordComponent, path: 'change-password', component: ChangePasswordComponent,
canActivate: [UnauthGuardService],
data: { titleId: 'setMasterPassword' }, data: { titleId: 'setMasterPassword' },
}, },
{ {

View File

@ -28,18 +28,18 @@
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<div class="form-group"> <div class="form-group">
<label for="newMasterPassword">{{'newMasterPass' | i18n}}</label> <label for="masterPassword">{{'newMasterPass' | i18n}}</label>
<input id="newMasterPassword" type="password" name="NewMasterPasswordHash" class="form-control mb-1" <input id="masterPassword" type="password" name="NewMasterPasswordHash" class="form-control mb-1"
[(ngModel)]="newMasterPassword" (input)="updatePasswordStrength()" required appInputVerbatim [(ngModel)]="masterPassword" (input)="updatePasswordStrength()" required appInputVerbatim
autocomplete="new-password"> autocomplete="new-password">
<app-password-strength [score]="masterPasswordScore" [showText]="true"></app-password-strength> <app-password-strength [score]="masterPasswordScore" [showText]="true"></app-password-strength>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="form-group"> <div class="form-group">
<label for="confirmNewMasterPassword">{{'confirmNewMasterPass' | i18n}}</label> <label for="masterPasswordRetype">{{'confirmNewMasterPass' | i18n}}</label>
<input id="confirmNewMasterPassword" type="password" name="ConfirmNewMasterPasswordHash" <input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
class="form-control" [(ngModel)]="confirmNewMasterPassword" required appInputVerbatim class="form-control" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
autocomplete="new-password"> autocomplete="new-password">
</div> </div>
</div> </div>

View File

@ -79,6 +79,16 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
} }
} }
async submit() {
const hasEncKey = await this.cryptoService.hasEncKey();
if (!hasEncKey) {
this.platformUtilsService.showToast('error', null, this.i18nService.t('updateKey'));
return;
}
await super.submit();
}
async setupSubmitActions() { async setupSubmitActions() {
if (this.currentMasterPassword == null || this.currentMasterPassword === '') { if (this.currentMasterPassword == null || this.currentMasterPassword === '') {
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
@ -90,7 +100,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
await this.syncService.fullSync(true); await this.syncService.fullSync(true);
} }
super.setupSubmitActions(); return super.setupSubmitActions();
} }
async performSubmitActions(newMasterPasswordHash: string, newKey: SymmetricCryptoKey, async performSubmitActions(newMasterPasswordHash: string, newKey: SymmetricCryptoKey,

View File

@ -3168,7 +3168,7 @@
"message": "Set Master Password" "message": "Set Master Password"
}, },
"ssoCompleteRegistration": { "ssoCompleteRegistration": {
"message": "In order to complete logging in with SSO, please set a master password below. Make sure to choose a strong password/passphrase and comply with all applied organizational policies." "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault."
}, },
"identifier": { "identifier": {
"message": "Identifier" "message": "Identifier"