Browse Source

UI: Redesigned 2fa login page

pull/6235/head
Vladyslav_Prykhodko 4 years ago
parent
commit
9c8913edb4
  1. 2
      ui-ngx/src/app/core/auth/auth.service.ts
  2. 18
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html
  3. 8
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss
  4. 34
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts
  5. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
ui-ngx/src/app/core/auth/auth.service.ts

@ -127,7 +127,7 @@ export class AuthService {
public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable<LoginResponse> { public checkTwoFaVerificationCode(providerType: TwoFactorAuthProviderType, verificationCode: number): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`, return this.http.post<LoginResponse>(`/api/auth/2fa/verification/check?providerType=${providerType}&verificationCode=${verificationCode}`,
null, defaultHttpOptions()).pipe( null, defaultHttpOptions(false, true)).pipe(
tap((loginResponse: LoginResponse) => { tap((loginResponse: LoginResponse) => {
this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true); this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, true);
} }

18
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html

@ -46,9 +46,18 @@
[attr.inputmode]="inputMode" [pattern]="pattern" [attr.inputmode]="inputMode" [pattern]="pattern"
autocomplete="off" autocomplete="off"
placeholder="{{ providersData.get(selectedProvider).placeholder | translate }}"/> placeholder="{{ providersData.get(selectedProvider).placeholder | translate }}"/>
<mat-error *ngIf="verificationForm.get('verificationCode').invalid"> <mat-error *ngIf="verificationForm.get('verificationCode').getError('required') ||
verificationForm.get('verificationCode').getError('minlength') ||
verificationForm.get('verificationCode').getError('maxlength') ||
verificationForm.get('verificationCode').getError('pattern')">
{{ 'security.2fa.dialog.verification-code-invalid' | translate }} {{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="verificationForm.get('verificationCode').getError('incorrectCode')">
{{ 'security.2fa.dialog.verification-code-incorrect' | translate }}
</mat-error>
<mat-error *ngIf="verificationForm.get('verificationCode').getError('tooManyRequest')">
{{ 'security.2fa.dialog.verification-code-many-request' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<span style="height: 50px;"></span> <span style="height: 50px;"></span>
@ -59,12 +68,12 @@
{{ 'action.continue' | translate }} {{ 'action.continue' | translate }}
</button> </button>
<span style="height: 16px;"></span> <span style="height: 16px;"></span>
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="8px"> <div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="8px" class="action-row">
<div *ngIf="showResendButton" fxFlex="50" fxLayoutAlign="center center"> <div *ngIf="showResendAction" fxFlex fxLayoutAlign="center center" class="action-resend">
<div *ngIf="countDownTime" class="timer"> <div *ngIf="countDownTime" class="timer">
{{ 'login.resend-code-wait' | translate : {time: countDownTime} }} {{ 'login.resend-code-wait' | translate : {time: countDownTime} }}
</div> </div>
<button *ngIf="!disabledResendButton" <button [fxShow]="!hideResendButton"
mat-button fxFlex mat-button fxFlex
(click)="sendCode($event)" (click)="sendCode($event)"
type="button"> type="button">
@ -73,6 +82,7 @@
</div> </div>
<button mat-button fxFlex <button mat-button fxFlex
type="button" type="button"
*ngIf="allowProviders.length > 1"
(click)="selectProvider(null)"> (click)="selectProvider(null)">
{{ 'login.try-another-way' | translate }} {{ 'login.try-another-way' | translate }}
</button> </button>

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

@ -60,6 +60,14 @@
font: 500 12px / 14px Roboto, "Helvetica Neue", sans-serif; font: 500 12px / 14px Roboto, "Helvetica Neue", sans-serif;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
} }
.action-row:nth-child(n) {
min-height: 36px;
.action-resend {
min-width: 50%;
}
}
} }
} }

34
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts

@ -28,6 +28,7 @@ import {
} from '@shared/models/two-factor-auth.models'; } from '@shared/models/two-factor-auth.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { interval, Subscription } from 'rxjs'; import { interval, Subscription } from 'rxjs';
import { isEqual } from '@core/utils';
@Component({ @Component({
selector: 'tb-two-factor-auth-login', selector: 'tb-two-factor-auth-login',
@ -40,14 +41,15 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
private prevProvider: TwoFactorAuthProviderType; private prevProvider: TwoFactorAuthProviderType;
private timer: Subscription; private timer: Subscription;
private minVerificationPeriod = 0; private minVerificationPeriod = 0;
private timerID: NodeJS.Timeout;
showResendButton = false; showResendAction = false;
selectedProvider: TwoFactorAuthProviderType; selectedProvider: TwoFactorAuthProviderType;
allowProviders: TwoFactorAuthProviderType[] = []; allowProviders: TwoFactorAuthProviderType[] = [];
providersData = twoFactorAuthProvidersLoginData; providersData = twoFactorAuthProvidersLoginData;
providerDescription = ''; providerDescription = '';
disabledResendButton = true; hideResendButton = true;
countDownTime = 0; countDownTime = 0;
maxLengthInput = 6; maxLengthInput = 6;
@ -88,7 +90,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
}); });
if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) { if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) {
this.sendCode(); this.sendCode();
this.showResendButton = true; this.showResendAction = true;
} }
this.timer = interval(1000).subscribe(() => this.updatedTime()); this.timer = interval(1000).subscribe(() => this.updatedTime());
} }
@ -96,12 +98,28 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
ngOnDestroy() { ngOnDestroy() {
super.ngOnDestroy(); super.ngOnDestroy();
this.timer.unsubscribe(); this.timer.unsubscribe();
clearTimeout(this.timerID);
} }
sendVerificationCode() { sendVerificationCode() {
if (this.verificationForm.valid && this.selectedProvider) { if (this.verificationForm.valid && this.selectedProvider) {
this.authService.checkTwoFaVerificationCode(this.selectedProvider, this.verificationForm.get('verificationCode').value).subscribe( this.authService.checkTwoFaVerificationCode(this.selectedProvider, this.verificationForm.get('verificationCode').value).subscribe(
() => {} () => {},
(error) => {
if (error.status === 400) {
this.verificationForm.get('verificationCode').setErrors({incorrectCode: true});
} else if (error.status === 429) {
this.verificationForm.get('verificationCode').setErrors({tooManyRequest: true});
this.timerID = setTimeout(() => {
let errors = this.verificationForm.get('verificationCode').errors;
delete errors.tooManyRequest;
if (isEqual(errors, {})) {
errors = null;
}
this.verificationForm.get('verificationCode').setErrors(errors);
}, 5000);
}
}
); );
} }
} }
@ -109,7 +127,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
selectProvider(type: TwoFactorAuthProviderType) { selectProvider(type: TwoFactorAuthProviderType) {
this.prevProvider = type === null ? this.selectedProvider : null; this.prevProvider = type === null ? this.selectedProvider : null;
this.selectedProvider = type; this.selectedProvider = type;
this.showResendButton = false; this.showResendAction = false;
if (type !== null) { if (type !== null) {
this.verificationForm.get('verificationCode').reset(); this.verificationForm.get('verificationCode').reset();
const providerConfig = this.providersInfo.find(config => config.type === type); const providerConfig = this.providersInfo.find(config => config.type === type);
@ -118,7 +136,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
}); });
if (type !== TwoFactorAuthProviderType.TOTP && type !== TwoFactorAuthProviderType.BACKUP_CODE) { if (type !== TwoFactorAuthProviderType.TOTP && type !== TwoFactorAuthProviderType.BACKUP_CODE) {
this.sendCode(); this.sendCode();
this.showResendButton = true; this.showResendAction = true;
this.minVerificationPeriod = providerConfig?.minVerificationCodeSendPeriod || 30; this.minVerificationPeriod = providerConfig?.minVerificationCodeSendPeriod || 30;
} }
if (type === TwoFactorAuthProviderType.BACKUP_CODE) { if (type === TwoFactorAuthProviderType.BACKUP_CODE) {
@ -150,7 +168,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.disabledResendButton = true; this.hideResendButton = true;
this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => { this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => {
this.countDownTime = this.minVerificationPeriod; this.countDownTime = this.minVerificationPeriod;
}, () => { }, () => {
@ -171,7 +189,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
if (this.countDownTime > 0) { if (this.countDownTime > 0) {
this.countDownTime--; this.countDownTime--;
if (this.countDownTime === 0) { if (this.countDownTime === 0) {
this.disabledResendButton = false; this.hideResendButton = false;
} }
} }
} }

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

@ -2633,6 +2633,8 @@
"totp-step-label": "Get app", "totp-step-label": "Get app",
"verification-code": "6-digit code", "verification-code": "6-digit code",
"verification-code-invalid": "Invalid verification code format", "verification-code-invalid": "Invalid verification code format",
"verification-code-incorrect": "Verification code is incorrect",
"verification-code-many-request": "Too many requests check verification code",
"verification-step-description": "Enter a 6-digit code we just sent to {{address}}", "verification-step-description": "Enter a 6-digit code we just sent to {{address}}",
"verification-step-label": "Verification" "verification-step-label": "Verification"
}, },

Loading…
Cancel
Save