Added set password flow to browser based SSO

This commit is contained in:
Matt Smith 2020-08-25 09:49:24 -05:00
parent a09bd4a3fe
commit 91ce6527c0
5 changed files with 196 additions and 9 deletions

View File

@ -0,0 +1,124 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a routerLink="/home">{{'cancel' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'appName' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'login' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
</div>
</header>
<content>
<div class="box">
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout>
<app-callout type="info" *ngIf="enforcedPolicyOptions">
{{'masterPasswordPolicyInEffect' | i18n}}
<ul>
<li *ngIf="enforcedPolicyOptions?.minComplexity > 0">
{{'policyInEffectMinComplexity' | i18n : getPasswordScoreAlertDisplay()}}
</li>
<li *ngIf="enforcedPolicyOptions?.minLength > 0">
{{'policyInEffectMinLength' | i18n : enforcedPolicyOptions?.minLength.toString()}}
</li>
<li *ngIf="enforcedPolicyOptions?.requireUpper">{{'policyInEffectUppercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireLower">{{'policyInEffectLowercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireNumbers">{{'policyInEffectNumbers' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireSpecial">{{'policyInEffectSpecial' | i18n : '!@#$%^&*'}}
</li>
</ul>
</app-callout>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<div class="box-content-row-flex">
<div class="row-main">
<label for="masterPassword">{{'masterPass' | i18n}}
<strong class="sub-label text-{{masterPasswordScoreColor}}"
*ngIf="masterPasswordScoreText">
{{masterPasswordScoreText}}
</strong>
</label>
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
(input)="updatePasswordStrength()" appInputVerbatim>
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
<div class="progress">
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
[ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
attr.aria-valuenow="{{masterPasswordScoreWidth}}">
</div>
</div>
</div>
</div>
<div class="box-footer">
{{'masterPassDesc' | i18n}}
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<div class="box-content-row-flex">
<div class="row-main">
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
class="monospaced" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
autocomplete="new-password">
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="box last">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="hint">{{'masterPassHint' | i18n}}</label>
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
</div>
</div>
<div class="box-footer">
{{'masterPassHintDesc' | i18n}}
</div>
</div>
</content>
</form>
<!-- <div class="content">
<img class="logo-image" alt="Bitwarden">
<p class="lead">{{'setMasterPassword' | i18n}}</p>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
<div class="buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading">
<i *ngIf="form.loading" class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"
aria-hidden="true"></i>
<span>{{'submit' | i18n}}</span>
</button>
<button class="btn block" (click)="logOut()">
<span>{{'logOut' | i18n}}</span>
</button>
</div>
</form> -->

View File

@ -0,0 +1,60 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ApiService } from 'jslib/abstractions/api.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { PolicyService } from 'jslib/abstractions/policy.service';
import { UserService } from 'jslib/abstractions/user.service';
import {
SetPasswordComponent as BaseSetPasswordComponent,
} from 'jslib/angular/components/set-password.component';
@Component({
selector: 'app-set-password',
templateUrl: 'set-password.component.html',
})
export class SetPasswordComponent extends BaseSetPasswordComponent {
constructor(apiService: ApiService, i18nService: I18nService,
cryptoService: CryptoService, messagingService: MessagingService,
userService: UserService, passwordGenerationService: PasswordGenerationService,
platformUtilsService: PlatformUtilsService, policyService: PolicyService, router: Router) {
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
platformUtilsService, policyService, router, apiService);
}
get masterPasswordScoreWidth() {
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
}
get masterPasswordScoreColor() {
switch (this.masterPasswordScore) {
case 4:
return 'success';
case 3:
return 'primary';
case 2:
return 'warning';
default:
return 'danger';
}
}
get masterPasswordScoreText() {
switch (this.masterPasswordScore) {
case 4:
return this.i18nService.t('strong');
case 3:
return this.i18nService.t('good');
case 2:
return this.i18nService.t('weak');
default:
return this.masterPasswordScore != null ? this.i18nService.t('weak') : null;
}
}
}

View File

@ -39,6 +39,7 @@ import { GroupingsComponent } from './vault/groupings.component';
import { PasswordHistoryComponent } from './vault/password-history.component'; import { PasswordHistoryComponent } from './vault/password-history.component';
import { ShareComponent } from './vault/share.component'; import { ShareComponent } from './vault/share.component';
import { ViewComponent } from './vault/view.component'; import { ViewComponent } from './vault/view.component';
import { SetPasswordComponent } from './accounts/set-password.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -86,6 +87,11 @@ const routes: Routes = [
canActivate: [LaunchGuardService], canActivate: [LaunchGuardService],
data: { state: 'sso' }, data: { state: 'sso' },
}, },
{
path: 'set-password',
component: SetPasswordComponent,
data: { state: 'set-password' },
},
{ {
path: 'register', path: 'register',
component: RegisterComponent, component: RegisterComponent,

View File

@ -45,6 +45,7 @@ import { GroupingsComponent } from './vault/groupings.component';
import { PasswordHistoryComponent } from './vault/password-history.component'; import { PasswordHistoryComponent } from './vault/password-history.component';
import { ShareComponent } from './vault/share.component'; import { ShareComponent } from './vault/share.component';
import { ViewComponent } from './vault/view.component'; import { ViewComponent } from './vault/view.component';
import { SetPasswordComponent } from './accounts/set-password.component';
import { A11yTitleDirective } from 'jslib/angular/directives/a11y-title.directive'; import { A11yTitleDirective } from 'jslib/angular/directives/a11y-title.directive';
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive'; import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
@ -209,6 +210,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
TwoFactorComponent, TwoFactorComponent,
SsoComponent, SsoComponent,
ViewComponent, ViewComponent,
SetPasswordComponent
], ],
entryComponents: [], entryComponents: [],
providers: [ providers: [

View File

@ -21,7 +21,7 @@ import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.s
import { CipherService } from 'jslib/abstractions/cipher.service'; import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service'; import { CollectionService } from 'jslib/abstractions/collection.service';
import { CryptoService } from 'jslib/abstractions/crypto.service'; import { CryptoService } from 'jslib/abstractions/crypto.service';
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service'; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib/abstractions/cryptoFunction.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service'; import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { EventService } from 'jslib/abstractions/event.service'; import { EventService } from 'jslib/abstractions/event.service';
import { ExportService } from 'jslib/abstractions/export.service'; import { ExportService } from 'jslib/abstractions/export.service';
@ -41,7 +41,6 @@ import { TokenService } from 'jslib/abstractions/token.service';
import { TotpService } from 'jslib/abstractions/totp.service'; import { TotpService } from 'jslib/abstractions/totp.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service'; import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
import { AutofillService } from '../../services/abstractions/autofill.service'; import { AutofillService } from '../../services/abstractions/autofill.service';
import BrowserMessagingService from '../../services/browserMessaging.service'; import BrowserMessagingService from '../../services/browserMessaging.service';
@ -50,6 +49,7 @@ import { AuthService } from 'jslib/services/auth.service';
import { ConstantsService } from 'jslib/services/constants.service'; import { ConstantsService } from 'jslib/services/constants.service';
import { SearchService } from 'jslib/services/search.service'; import { SearchService } from 'jslib/services/search.service';
import { StateService } from 'jslib/services/state.service'; import { StateService } from 'jslib/services/state.service';
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
import { Analytics } from 'jslib/misc/analytics'; import { Analytics } from 'jslib/misc/analytics';
@ -72,7 +72,7 @@ export const authService = new AuthService(getBgService<CryptoService>('cryptoSe
messagingService, getBgService<VaultTimeoutService>('vaultTimeoutService')(), null); messagingService, getBgService<VaultTimeoutService>('vaultTimeoutService')(), null);
export const searchService = new PopupSearchService(getBgService<SearchService>('searchService')(), export const searchService = new PopupSearchService(getBgService<SearchService>('searchService')(),
getBgService<CipherService>('cipherService')(), getBgService<PlatformUtilsService>('platformUtilsService')()); getBgService<CipherService>('cipherService')(), getBgService<PlatformUtilsService>('platformUtilsService')());
export const cryptoFunctionService: CryptoFunctionService = new WebCryptoFunctionService(window, export const cryptoFunctionService = new WebCryptoFunctionService(window,
getBgService<PlatformUtilsService>('platformUtilsService')()); getBgService<PlatformUtilsService>('platformUtilsService')());
export function initFactory(i18nService: I18nService, storageService: StorageService, export function initFactory(i18nService: I18nService, storageService: StorageService,
@ -125,7 +125,7 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
{ provide: AuthServiceAbstraction, useValue: authService }, { provide: AuthServiceAbstraction, useValue: authService },
{ provide: StateServiceAbstraction, useValue: stateService }, { provide: StateServiceAbstraction, useValue: stateService },
{ provide: SearchServiceAbstraction, useValue: searchService }, { provide: SearchServiceAbstraction, useValue: searchService },
{ provide: CryptoFunctionService, useValue: cryptoFunctionService }, { provide: CryptoFunctionServiceAbstraction, useValue: cryptoFunctionService },
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] }, { provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] }, { provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] }, { provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },
@ -135,11 +135,6 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
{ provide: TokenService, useFactory: getBgService<TokenService>('tokenService'), deps: [] }, { provide: TokenService, useFactory: getBgService<TokenService>('tokenService'), deps: [] },
{ provide: I18nService, useFactory: getBgService<I18nService>('i18nService'), deps: [] }, { provide: I18nService, useFactory: getBgService<I18nService>('i18nService'), deps: [] },
{ provide: CryptoService, useFactory: getBgService<CryptoService>('cryptoService'), deps: [] }, { provide: CryptoService, useFactory: getBgService<CryptoService>('cryptoService'), deps: [] },
{
provide: CryptoFunctionService,
useFactory: getBgService<CryptoFunctionService>('cryptoFunctionService'),
deps: [],
},
{ provide: EventService, useFactory: getBgService<EventService>('eventService'), deps: [] }, { provide: EventService, useFactory: getBgService<EventService>('eventService'), deps: [] },
{ provide: PolicyService, useFactory: getBgService<PolicyService>('policyService'), deps: [] }, { provide: PolicyService, useFactory: getBgService<PolicyService>('policyService'), deps: [] },
{ {