Browse Source

UI: 2fa improvement mobile view; remove required maxVerificationFailuresBeforeUserLockout

pull/6235/head
Vladyslav_Prykhodko 4 years ago
parent
commit
67e969fd75
  1. 5
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html
  2. 9
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss
  3. 1
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts
  4. 20
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss
  5. 23
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html
  6. 23
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html
  7. 7
      ui-ngx/src/app/modules/home/pages/security/security.component.html
  8. 6
      ui-ngx/src/app/modules/home/pages/security/security.component.scss
  9. 22
      ui-ngx/src/app/modules/home/pages/security/security.component.ts
  10. 14
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss
  11. 15
      ui-ngx/src/app/shared/models/two-factor-auth.models.ts
  12. 7
      ui-ngx/src/assets/locale/locale.constant-en_US.json

5
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html

@ -140,10 +140,7 @@
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label>
<input matInput required formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535">
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('required')">
{{ 'admin.2fa.max-verification-failures-before-user-lockout-required' | translate }}
</mat-error>
<input matInput formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535">
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('pattern')
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('min')
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('max')">

9
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss

@ -16,8 +16,7 @@
@import "../../../../../scss/constants";
:host{
:host {
mat-card.settings-card {
@media #{$mat-md} {
width: 90%;
@ -48,17 +47,22 @@
.mat-expansion-panel {
box-shadow: none;
margin: 1px 0 0;
&.provider {
overflow: inherit;
.mat-expansion-panel-header {
padding: 0 24px 0 8px;
&.mat-expanded {
height: 48px;
}
.mat-slide-toggle {
margin-right: 8px;
}
}
.mat-expansion-panel-header-title {
height: 40px;
}
@ -73,6 +77,7 @@
.mat-expansion-panel-header > .mat-content {
overflow: inherit;
}
.mat-expansion-panel-body {
padding: 0 16px 8px 8px;
}

1
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts

@ -114,7 +114,6 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
private build2faSettingsForm(): void {
this.twoFaFormGroup = this.fb.group({
maxVerificationFailuresBeforeUserLockout: [30, [
Validators.required,
Validators.pattern(/^\d*$/),
Validators.min(0),
Validators.max(65535)

20
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss

@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host{
@import "../../../../../../scss/constants";
:host {
.mat-toolbar > h2 {
font-weight: 400;
letter-spacing: 0.25px;
@ -30,9 +32,11 @@
&:not(:first-of-type) {
margin: 0 0 8px;
}
&.description {
margin: 0;
color: rgba(0, 0, 0, 0.54);
&:last-of-type {
margin-bottom: 24px;
}
@ -45,6 +49,13 @@
.code-container {
max-width: 170px;
&.full-width-xs {
@media #{$mat-xs} {
max-width: 100%;
width: 100%;
}
}
}
.result-title {
@ -63,6 +74,7 @@
.step-description {
max-width: 450px;
&.input {
margin: 12px 0 0;
}
@ -78,26 +90,30 @@
.backup-code {
max-width: 500px;
.container {
max-width: 500px;
margin: 40px 0 8px;
.code {
letter-spacing: 0.25px;
padding: 0 24px;
margin-bottom: 16px;
font-family: Roboto Mono, "Helvetica Neue", monospace;
&.even {
text-align: right;
}
}
}
.action-buttons {
margin-bottom: 40px;
}
}
& ::ng-deep {
.mat-horizontal-stepper-header{
.mat-horizontal-stepper-header {
pointer-events: none !important;
}
}

23
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html

@ -64,8 +64,8 @@
<p class="mat-body step-description input">
{{ 'security.2fa.dialog.verification-step-description' | translate : {address: emailConfigForm.get('email').value} }}
</p>
<div fxLayout="row" fxLayoutAlign="space-between center">
<mat-form-field fxFlex class="mat-block code-container" hideRequiredMarker floatLabel="always">
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start end">
<mat-form-field fxFlex class="mat-block code-container full-width-xs" hideRequiredMarker floatLabel="always">
<mat-label></mat-label>
<input matInput formControlName="verificationCode"
maxlength="6" type="text" required
@ -76,12 +76,19 @@
{{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error>
</mat-form-field>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || emailVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
<div fxLayoutGap="8px">
<button mat-button
matStepperPrevious
[disabled]="(isLoading$ | async)">
{{ 'action.back' | translate }}
</button>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || emailVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
</div>
</div>
</form>
</mat-step>

23
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html

@ -66,8 +66,8 @@
<p class="mat-body step-description input">
{{ 'security.2fa.dialog.verification-step-description' | translate : {address: smsConfigForm.get('phone').value} }}
</p>
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block code-container" floatLabel="always" hideRequiredMarker>
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start end">
<mat-form-field fxFlex class="mat-block code-container full-width-xs" floatLabel="always" hideRequiredMarker>
<mat-label></mat-label>
<input matInput formControlName="verificationCode"
maxlength="6" type="text" required
@ -78,12 +78,19 @@
{{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error>
</mat-form-field>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || smsVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
<div fxLayoutGap="8px">
<button mat-button
matStepperPrevious
[disabled]="(isLoading$ | async)">
{{ 'action.back' | translate }}
</button>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || smsVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
</div>
</div>
</form>
</mat-step>

7
ui-ngx/src/app/modules/home/pages/security/security.component.html

@ -56,9 +56,14 @@
<div class="provider">
<h4 class="provider-title">{{ providersData.get(provider).name | translate }}</h4>
<div fxLayout="row" fxLayoutAlign="space-between start">
<div class="mat-body-1 description">
<div class="mat-body-1 description" *ngIf="!twoFactorAuth.get(provider).value; else providerInfo">
{{ providersData.get(provider).description | translate }}
</div>
<ng-template #providerInfo>
<div class="mat-body-1 description">
{{ providersData.get(provider).activatedHint | translate: providerDataInfo(provider) }}
</div>
</ng-template>
<mat-slide-toggle [formControlName]="provider"
(click)="confirm2FAChange($event, provider)">
</mat-slide-toggle>

6
ui-ngx/src/app/modules/home/pages/security/security.component.scss

@ -19,6 +19,7 @@
.profile-container {
padding: 8px;
}
mat-card.profile-card {
@media #{$mat-gt-sm} {
width: 70%;
@ -29,16 +30,19 @@
@media #{$mat-gt-xl} {
width: 45%;
}
.mat-subheader {
line-height: 24px;
color: rgba(0,0,0,0.54);
color: rgba(0, 0, 0, 0.54);
font-size: 14px;
font-weight: 400;
}
.profile-last-login-ts {
font-size: 16px;
font-weight: 400;
}
.profile-btn-subtext {
font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif;
letter-spacing: 0.25px;

22
ui-ngx/src/app/modules/home/pages/security/security.component.ts

@ -30,6 +30,9 @@ import { ClipboardService } from 'ngx-clipboard';
import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service';
import {
AccountTwoFaSettings,
BackupCodeTwoFactorAuthAccountConfig,
EmailTwoFactorAuthAccountConfig,
SmsTwoFactorAuthAccountConfig,
twoFactorAuthProvidersData,
TwoFactorAuthProviderType
} from '@shared/models/two-factor-auth.models';
@ -231,4 +234,23 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro
}
});
}
providerDataInfo(provider: TwoFactorAuthProviderType) {
const info = {info: null};
const providerConfig = this.accountConfig.configs[provider];
if (isDefinedAndNotNull(providerConfig)) {
switch (provider) {
case TwoFactorAuthProviderType.EMAIL:
info.info = (providerConfig as EmailTwoFactorAuthAccountConfig).email;
break;
case TwoFactorAuthProviderType.SMS:
info.info = (providerConfig as SmsTwoFactorAuthAccountConfig).phoneNumber;
break;
case TwoFactorAuthProviderType.BACKUP_CODE:
info.info = (providerConfig as BackupCodeTwoFactorAuthAccountConfig).codesLeft;
break;
}
}
return info;
}
}

14
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss

@ -18,8 +18,10 @@
:host {
display: flex;
flex: 1 1 0;
.tb-two-factor-auth-login-content {
background-color: #eee;
.tb-two-factor-auth-login-card {
padding: 48px 48px 48px 16px;
@ -27,7 +29,7 @@
width: 450px !important;
}
.mat-card-title{
.mat-card-title {
font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif;
}
@ -46,7 +48,7 @@
margin-top: 16px;
}
.providers-container{
.providers-container {
padding: 0;
.mat-body {
@ -61,19 +63,23 @@
}
}
}
::ng-deep{
::ng-deep {
button.provider {
text-align: start;
font-weight: 400;
&:not(.mat-button-disabled) {
border-color: rgba(255, 255, 255, .8);
}
.icon{
.icon {
height: 18px;
width: 18px;
vertical-align: sub;
}
}
.mat-form-field-invalid .mat-hint {
margin-top: 20px;
}

15
ui-ngx/src/app/shared/models/two-factor-auth.models.ts

@ -103,9 +103,10 @@ export interface TwoFaProviderInfo {
export interface TwoFactorAuthProviderData {
name: string;
description: string;
activatedHint: string;
}
export interface TwoFactorAuthProviderLoginData extends TwoFactorAuthProviderData {
export interface TwoFactorAuthProviderLoginData extends Omit<TwoFactorAuthProviderData, 'activatedHint'> {
icon: string;
placeholder: string;
}
@ -115,25 +116,29 @@ export const twoFactorAuthProvidersData = new Map<TwoFactorAuthProviderType, Two
[
TwoFactorAuthProviderType.TOTP, {
name: 'security.2fa.provider.totp',
description: 'security.2fa.provider.totp-description'
description: 'security.2fa.provider.totp-description',
activatedHint: 'security.2fa.provider.totp-hint'
}
],
[
TwoFactorAuthProviderType.SMS, {
name: 'security.2fa.provider.sms',
description: 'security.2fa.provider.sms-description'
description: 'security.2fa.provider.sms-description',
activatedHint: 'security.2fa.provider.sms-hint'
}
],
[
TwoFactorAuthProviderType.EMAIL, {
name: 'security.2fa.provider.email',
description: 'security.2fa.provider.email-description'
description: 'security.2fa.provider.email-description',
activatedHint: 'security.2fa.provider.email-hint'
}
],
[
TwoFactorAuthProviderType.BACKUP_CODE, {
name: 'security.2fa.provider.backup_code',
description: 'security.2fa.provider.backup-code-description'
description: 'security.2fa.provider.backup-code-description',
activatedHint: 'security.2fa.provider.backup-code-hint'
}
]
]

7
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -321,7 +321,6 @@
"issuer-name-required": "Issuer name is required.",
"max-verification-failures-before-user-lockout": "Max verification failures before user lockout",
"max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.",
"max-verification-failures-before-user-lockout-required": "Max verification failures is required.",
"number-of-checking-attempts": "Number of checking attempts",
"number-of-checking-attempts-pattern": "Number of checking attempts must be a positive integer.",
"number-of-checking-attempts-required": "Number of checking attempts is required.",
@ -2624,12 +2623,16 @@
"provider": {
"email": "Email",
"email-description": "Use a security code sent to your email address to authenticate.",
"email-hint": "Authentication codes are sent via email to {{ info }}",
"sms": "SMS",
"sms-description": "Use your phone to authenticate. We'll send you a security code via SMS message when you log in.",
"sms-hint": "Authentication codes are sent by text message to {{ info }}",
"totp": "Authenticator app",
"totp-description": "Use apps like Google Authenticator, Authy, or Duo on your phone to authenticate. It will generate a security code for logging in.",
"totp-hint": "Authenticator app is set up for your account",
"backup_code": "Backup code",
"backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling."
"backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling.",
"backup-code-hint": "{{ info }} single-use codes are active at this time"
}
}
},

Loading…
Cancel
Save