Browse Source

UI: Add 2FA resend code timer

pull/6235/head
Vladyslav_Prykhodko 4 years ago
parent
commit
cc310887c6
  1. 54
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html
  2. 70
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss
  3. 43
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts
  4. 25
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html
  5. 9
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss
  6. 39
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.ts
  7. 6
      ui-ngx/src/app/shared/models/two-factor-auth.models.ts
  8. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

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

@ -38,7 +38,7 @@
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center"> <mat-panel-title fxLayoutAlign="start center">
<mat-slide-toggle <mat-slide-toggle
(click)="toggleProviders($event, i)" (click)="toggleExtensionPanel($event, i, provider.get('enable').value)"
formControlName="enable"> formControlName="enable">
</mat-slide-toggle> </mat-slide-toggle>
{{ provider.value.providerType }} {{ provider.value.providerType }}
@ -114,6 +114,17 @@
{{ 'admin.2fa.total-allowed-time-for-verification-pattern' | translate }} {{ 'admin.2fa.total-allowed-time-for-verification-pattern' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.retry-verification-code-period</mat-label>
<input matInput required formControlName="minVerificationCodeSendPeriod" type="number" step="1" min="5">
<mat-error *ngIf="twoFaFormGroup.get('minVerificationCodeSendPeriod').hasError('required')">
{{ 'admin.2fa.retry-verification-code-period-required' | translate }}
</mat-error>
<mat-error *ngIf="twoFaFormGroup.get('minVerificationCodeSendPeriod').hasError('pattern')
|| twoFaFormGroup.get('minVerificationCodeSendPeriod').hasError('min')">
{{ 'admin.2fa.retry-verification-code-period-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label> <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"> <input matInput required formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535">
@ -127,48 +138,11 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<mat-expansion-panel class="provider"> <mat-expansion-panel class="provider">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center"> <mat-panel-title fxLayoutAlign="start center">
<mat-slide-toggle <mat-slide-toggle (click)="toggleExtensionPanel($event, 3, twoFaFormGroup.get('verificationCodeCheckRateLimitEnable').value)"
(click)="toggleExtensionPanel($event, 0, twoFaFormGroup.get('verificationCodeSendRateLimitEnable').value)"
formControlName="verificationCodeSendRateLimitEnable">
</mat-slide-toggle>
{{ 'admin.2fa.verification-code-send-rate-limit' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.number-of-send-attempts</mat-label>
<input matInput formControlName="verificationCodeSendRateLimitNumber" type="number" step="1" min="1" required>
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimitNumber').hasError('required')">
{{ 'admin.2fa.number-of-send-attempts-required' | translate }}
</mat-error>
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimitNumber').hasError('pattern')
|| twoFaFormGroup.get('verificationCodeSendRateLimitNumber').hasError('min')">
{{ 'admin.2fa.number-of-send-attempts-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.within-time</mat-label>
<input matInput formControlName="verificationCodeSendRateLimitTime" type="number" step="1" min="1" required>
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimitTime').hasError('required')">
{{ 'admin.2fa.within-time-required' | translate }}
</mat-error>
<mat-error *ngIf="twoFaFormGroup.get('verificationCodeSendRateLimitTime').hasError('pattern')
|| twoFaFormGroup.get('verificationCodeSendRateLimitTime').hasError('min')">
{{ 'admin.2fa.within-time-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
<mat-divider></mat-divider>
<mat-expansion-panel class="provider">
<mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center">
<mat-slide-toggle (click)="toggleExtensionPanel($event, 1, twoFaFormGroup.get('verificationCodeCheckRateLimitEnable').value)"
formControlName="verificationCodeCheckRateLimitEnable"> formControlName="verificationCodeCheckRateLimitEnable">
</mat-slide-toggle> </mat-slide-toggle>
{{ 'admin.2fa.verification-code-check-rate-limit' | translate }} {{ 'admin.2fa.verification-code-check-rate-limit' | translate }}

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

@ -14,47 +14,55 @@
* limitations under the License. * limitations under the License.
*/ */
:host{ @import "../../../../../scss/constants";
.fields-group { :host{
margin: 8px 0;
border: 1px groove rgba(0, 0, 0, .25);
border-radius: 4px;
position: relative;
padding-bottom: 8px;
legend { mat-card.settings-card {
color: rgba(0, 0, 0, .7); @media #{$mat-md} {
width: fit-content; width: 90%;
margin: 0 8px;
} }
.input-row { .fields-group {
padding: 8px 8px 0; margin: 8px 0;
} border: 1px groove rgba(0, 0, 0, .25);
border-radius: 4px;
position: relative;
padding-bottom: 8px;
legend {
color: rgba(0, 0, 0, .7);
width: fit-content;
margin: 0 8px;
}
.input-row {
padding: 8px 8px 0;
}
&:last-of-type { &:last-of-type {
margin-bottom: 24px; margin-bottom: 24px;
}
} }
}
.mat-expansion-panel { .mat-expansion-panel {
box-shadow: none; box-shadow: none;
margin: 1px 0 0; margin: 1px 0 0;
&.provider { &.provider {
overflow: inherit; overflow: inherit;
.mat-expansion-panel-header { .mat-expansion-panel-header {
padding: 0 24px 0 8px; padding: 0 24px 0 8px;
&.mat-expanded { &.mat-expanded {
height: 48px; height: 48px;
}
.mat-slide-toggle {
margin-right: 8px;
}
} }
.mat-slide-toggle { .mat-expansion-panel-header-title {
margin-right: 8px; height: 40px;
} }
} }
.mat-expansion-panel-header-title {
height: 40px;
}
} }
} }
} }

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

@ -35,7 +35,7 @@ import { MatExpansionPanel } from '@angular/material/expansion';
@Component({ @Component({
selector: 'tb-2fa-settings', selector: 'tb-2fa-settings',
templateUrl: './two-factor-auth-settings.component.html', templateUrl: './two-factor-auth-settings.component.html',
styleUrls: ['./two-factor-auth-settings.component.scss', './settings-card.scss'] styleUrls: [ './settings-card.scss', './two-factor-auth-settings.component.scss']
}) })
export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy {
@ -73,7 +73,6 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
if (this.twoFaFormGroup.valid) { if (this.twoFaFormGroup.valid) {
const setting = this.twoFaFormGroup.value as TwoFactorAuthSettingsForm; const setting = this.twoFaFormGroup.value as TwoFactorAuthSettingsForm;
this.joinRateLimit(setting, 'verificationCodeCheckRateLimit'); this.joinRateLimit(setting, 'verificationCodeCheckRateLimit');
this.joinRateLimit(setting, 'verificationCodeSendRateLimit');
const providers = setting.providers.filter(provider => provider.enable); const providers = setting.providers.filter(provider => provider.enable);
providers.forEach(provider => delete provider.enable); providers.forEach(provider => delete provider.enable);
const config = Object.assign(setting, {providers}); const config = Object.assign(setting, {providers});
@ -91,18 +90,14 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
} }
} }
toggleProviders($event: Event, i: number): void { toggleExtensionPanel($event: Event, index: number, currentState: boolean) {
this.toggleExtensionPanel($event, i + 2, this.providersForm.at(i).get('enable').value);
}
toggleExtensionPanel($event: Event, i: number, currentState: boolean) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
if (currentState) { if (currentState) {
this.getByIndexPanel(i).close(); this.getByIndexPanel(index).close();
} else { } else {
this.getByIndexPanel(i).open(); this.getByIndexPanel(index).open();
} }
} }
@ -130,9 +125,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
verificationCodeCheckRateLimitEnable: [false], verificationCodeCheckRateLimitEnable: [false],
verificationCodeCheckRateLimitNumber: ['3', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]], verificationCodeCheckRateLimitNumber: ['3', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]],
verificationCodeCheckRateLimitTime: ['900', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]], verificationCodeCheckRateLimitTime: ['900', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]],
verificationCodeSendRateLimitEnable: [false], minVerificationCodeSendPeriod: ['30', [Validators.required, Validators.min(5), Validators.pattern(/^\d*$/)]],
verificationCodeSendRateLimitNumber: ['1', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]],
verificationCodeSendRateLimitTime: ['60', [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]],
providers: this.fb.array([]) providers: this.fb.array([])
}); });
Object.values(TwoFactorAuthProviderType).forEach(provider => { Object.values(TwoFactorAuthProviderType).forEach(provider => {
@ -149,43 +142,25 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
this.twoFaFormGroup.get('verificationCodeCheckRateLimitTime').disable({emitEvent: false}); this.twoFaFormGroup.get('verificationCodeCheckRateLimitTime').disable({emitEvent: false});
} }
}); });
this.twoFaFormGroup.get('verificationCodeSendRateLimitEnable').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
if (value) {
this.twoFaFormGroup.get('verificationCodeSendRateLimitNumber').enable({emitEvent: false});
this.twoFaFormGroup.get('verificationCodeSendRateLimitTime').enable({emitEvent: false});
} else {
this.twoFaFormGroup.get('verificationCodeSendRateLimitNumber').disable({emitEvent: false});
this.twoFaFormGroup.get('verificationCodeSendRateLimitTime').disable({emitEvent: false});
}
});
} }
private setAuthConfigFormValue(settings: TwoFactorAuthSettings) { private setAuthConfigFormValue(settings: TwoFactorAuthSettings) {
const [checkRateLimitNumber, checkRateLimitTime] = this.splitRateLimit(settings.verificationCodeCheckRateLimit); const [checkRateLimitNumber, checkRateLimitTime] = this.splitRateLimit(settings.verificationCodeCheckRateLimit);
const [sendRateLimitNumber, sendRateLimitTime] = this.splitRateLimit(settings.verificationCodeSendRateLimit);
const allowProvidersConfig = settings.providers.map(provider => provider.providerType); const allowProvidersConfig = settings.providers.map(provider => provider.providerType);
const processFormValue: TwoFactorAuthSettingsForm = Object.assign(deepClone(settings), { const processFormValue: TwoFactorAuthSettingsForm = Object.assign(deepClone(settings), {
verificationCodeCheckRateLimitEnable: checkRateLimitNumber > 0, verificationCodeCheckRateLimitEnable: checkRateLimitNumber > 0,
verificationCodeCheckRateLimitNumber: checkRateLimitNumber || 3, verificationCodeCheckRateLimitNumber: checkRateLimitNumber || 3,
verificationCodeCheckRateLimitTime: checkRateLimitTime || 900, verificationCodeCheckRateLimitTime: checkRateLimitTime || 900,
verificationCodeSendRateLimitEnable: sendRateLimitNumber > 0,
verificationCodeSendRateLimitNumber: sendRateLimitNumber || 1,
verificationCodeSendRateLimitTime: sendRateLimitTime || 60,
providers: [] providers: []
}); });
if (sendRateLimitNumber > 0) {
this.getByIndexPanel(0).open();
}
if (checkRateLimitNumber > 0) { if (checkRateLimitNumber > 0) {
this.getByIndexPanel(1).open(); this.getByIndexPanel(3).open();
} }
Object.values(TwoFactorAuthProviderType).forEach((provider, index) => { Object.values(TwoFactorAuthProviderType).forEach((provider, index) => {
const findIndex = allowProvidersConfig.indexOf(provider); const findIndex = allowProvidersConfig.indexOf(provider);
if (findIndex > -1) { if (findIndex > -1) {
processFormValue.providers.push(Object.assign(settings.providers[findIndex], {enable: true})); processFormValue.providers.push(Object.assign(settings.providers[findIndex], {enable: true}));
this.getByIndexPanel(index + 2).open(); this.getByIndexPanel(index).open();
} else { } else {
processFormValue.providers.push({enable: false}); processFormValue.providers.push({enable: false});
} }
@ -203,9 +178,9 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
formControlConfig.issuerName = [{value: 'ThingsBoard', disabled: true}, Validators.required]; formControlConfig.issuerName = [{value: 'ThingsBoard', disabled: true}, Validators.required];
break; break;
case TwoFactorAuthProviderType.SMS: case TwoFactorAuthProviderType.SMS:
formControlConfig.smsVerificationMessageTemplate = [{value: 'Verification code: ${сode}', disabled: true}, [ formControlConfig.smsVerificationMessageTemplate = [{value: 'Verification code: ${code}', disabled: true}, [
Validators.required, Validators.required,
Validators.pattern(/\${сode}/) Validators.pattern(/\${code}/)
]]; ]];
formControlConfig.verificationCodeLifetime = [{value: 120, disabled: true}, [ formControlConfig.verificationCodeLifetime = [{value: 120, disabled: true}, [
Validators.required, Validators.required,

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

@ -46,20 +46,25 @@
inputmode="numeric" pattern="[0-9]*" inputmode="numeric" pattern="[0-9]*"
autocomplete="off" autocomplete="off"
placeholder="{{ 'security.2fa.dialog.verification-code' | translate }}"/> placeholder="{{ 'security.2fa.dialog.verification-code' | translate }}"/>
<mat-error *ngIf="verificationForm.get('verificationCode').invalid"> <button *ngIf="selectedProvider !== twoFactorAuthProvider.TOTP"
mat-button matSuffix
(click)="sendCode($event)"
[disabled]="disabledResendButton"
type="button" tabindex="-1">
{{ 'login.resend-code' | translate }}
</button>
<mat-error *ngIf="verificationForm.get('verificationCode').invalid" style="margin-bottom: 8px">
{{ 'security.2fa.dialog.verification-code-invalid' | translate }} {{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="verificationForm.get('verificationCode').invalid && selectedProvider !== twoFactorAuthProvider.TOTP && countDownTime" class="timer">
{{ 'login.resend-code-wait' | translate : {time: countDownTime} }}
</mat-error>
<mat-hint *ngIf="selectedProvider !== twoFactorAuthProvider.TOTP && countDownTime" class="timer">
{{ 'login.resend-code-wait' | translate : {time: countDownTime} }}
</mat-hint>
</mat-form-field> </mat-form-field>
<div fxLayoutAlign="start center" *ngIf="selectedProvider !== twoFactorAuthProvider.TOTP">
<button
mat-button
(click)="sendCode()"
type="button">
{{ 'login.resend-code' | translate }}
</button>
</div>
</div> </div>
<span style="height: 40px;"></span> <span style="height: 50px;"></span>
<div fxLayout="column" fxLayoutGap="8px"> <div fxLayout="column" fxLayoutGap="8px">
<button mat-raised-button <button mat-raised-button
color="accent" color="accent"

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

@ -53,6 +53,12 @@
padding-bottom: 8px; padding-bottom: 8px;
} }
} }
.timer {
font: 400 12px / 14px Roboto, "Helvetica Neue", sans-serif;
letter-spacing: 0.4px;
color: rgba(255, 255, 255, 0.8);
}
} }
} }
::ng-deep{ ::ng-deep{
@ -68,5 +74,8 @@
vertical-align: sub; vertical-align: sub;
} }
} }
.mat-form-field-invalid .mat-hint {
margin-top: 20px;
}
} }
} }

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

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { AuthService } from '@core/auth/auth.service'; import { AuthService } from '@core/auth/auth.service';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -27,16 +27,19 @@ import {
TwoFaProviderInfo TwoFaProviderInfo
} 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';
@Component({ @Component({
selector: 'tb-two-factor-auth-login', selector: 'tb-two-factor-auth-login',
templateUrl: './two-factor-auth-login.component.html', templateUrl: './two-factor-auth-login.component.html',
styleUrls: ['./two-factor-auth-login.component.scss'] styleUrls: ['./two-factor-auth-login.component.scss']
}) })
export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit { export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit, OnDestroy {
private providersInfo: TwoFaProviderInfo[]; private providersInfo: TwoFaProviderInfo[];
private prevProvider: TwoFactorAuthProviderType; private prevProvider: TwoFactorAuthProviderType;
private timer: Subscription;
private minVerificationPeriod = 0;
selectedProvider: TwoFactorAuthProviderType; selectedProvider: TwoFactorAuthProviderType;
twoFactorAuthProvider = TwoFactorAuthProviderType; twoFactorAuthProvider = TwoFactorAuthProviderType;
@ -44,6 +47,8 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
providersData = twoFactorAuthProvidersLoginData; providersData = twoFactorAuthProvidersLoginData;
providerDescription = ''; providerDescription = '';
disabledResendButton = true;
countDownTime = 0;
verificationForm = this.fb.group({ verificationForm = this.fb.group({
verificationCode: ['', [ verificationCode: ['', [
@ -72,6 +77,7 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, {
contact: providerConfig.contact contact: providerConfig.contact
}); });
this.minVerificationPeriod = providerConfig?.minVerificationCodeSendPeriod || 30;
} }
this.allowProviders.push(providerConfig.type); this.allowProviders.push(providerConfig.type);
} }
@ -79,6 +85,12 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) { if (this.selectedProvider !== TwoFactorAuthProviderType.TOTP) {
this.sendCode(); this.sendCode();
} }
this.timer = interval(1000).subscribe(() => this.updatedTime());
}
ngOnDestroy() {
super.ngOnDestroy();
this.timer.unsubscribe();
} }
sendVerificationCode() { sendVerificationCode() {
@ -93,18 +105,28 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
this.prevProvider = type === null ? this.selectedProvider : null; this.prevProvider = type === null ? this.selectedProvider : null;
this.selectedProvider = type; this.selectedProvider = type;
if (type !== null) { if (type !== null) {
this.verificationForm.get('verificationCode').reset();
const providerConfig = this.providersInfo.find(config => config.type === type); const providerConfig = this.providersInfo.find(config => config.type === type);
this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, { this.providerDescription = this.translate.instant(this.providersData.get(providerConfig.type).description, {
contact: providerConfig.contact contact: providerConfig.contact
}); });
this.minVerificationPeriod = providerConfig?.minVerificationCodeSendPeriod || 30;
if (type !== TwoFactorAuthProviderType.TOTP) { if (type !== TwoFactorAuthProviderType.TOTP) {
this.sendCode(); this.sendCode();
} }
} }
} }
sendCode() { sendCode($event?: Event) {
this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => {}); if ($event) {
$event.stopPropagation();
}
this.disabledResendButton = true;
this.twoFactorAuthService.requestTwoFaVerificationCodeSend(this.selectedProvider).subscribe(() => {
this.countDownTime = this.minVerificationPeriod;
}, () => {
this.countDownTime = 30;
});
} }
cancelLogin() { cancelLogin() {
@ -115,4 +137,13 @@ export class TwoFactorAuthLoginComponent extends PageComponent implements OnInit
this.authService.logout(); this.authService.logout();
} }
} }
private updatedTime() {
if (this.countDownTime > 0) {
this.countDownTime--;
if (this.countDownTime === 0) {
this.disabledResendButton = false;
}
}
}
} }

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

@ -20,7 +20,7 @@ export interface TwoFactorAuthSettings {
totalAllowedTimeForVerification: number; totalAllowedTimeForVerification: number;
useSystemTwoFactorAuthSettings: boolean; useSystemTwoFactorAuthSettings: boolean;
verificationCodeCheckRateLimit: string; verificationCodeCheckRateLimit: string;
verificationCodeSendRateLimit: string; minVerificationCodeSendPeriod: number;
} }
export interface TwoFactorAuthSettingsForm extends TwoFactorAuthSettings{ export interface TwoFactorAuthSettingsForm extends TwoFactorAuthSettings{
@ -28,9 +28,6 @@ export interface TwoFactorAuthSettingsForm extends TwoFactorAuthSettings{
verificationCodeCheckRateLimitEnable: boolean; verificationCodeCheckRateLimitEnable: boolean;
verificationCodeCheckRateLimitNumber: number; verificationCodeCheckRateLimitNumber: number;
verificationCodeCheckRateLimitTime: number; verificationCodeCheckRateLimitTime: number;
verificationCodeSendRateLimitEnable: boolean;
verificationCodeSendRateLimitNumber: number;
verificationCodeSendRateLimitTime: number;
} }
export type TwoFactorAuthProviderConfig = Partial<TotpTwoFactorAuthProviderConfig | SmsTwoFactorAuthProviderConfig | export type TwoFactorAuthProviderConfig = Partial<TotpTwoFactorAuthProviderConfig | SmsTwoFactorAuthProviderConfig |
@ -93,6 +90,7 @@ export interface TwoFaProviderInfo {
type: TwoFactorAuthProviderType; type: TwoFactorAuthProviderType;
default: boolean; default: boolean;
contact?: string; contact?: string;
minVerificationCodeSendPeriod?: number;
} }
export interface TwoFactorAuthProviderData { export interface TwoFactorAuthProviderData {

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

@ -323,10 +323,10 @@
"number-of-checking-attempts": "Number of checking attempts", "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-pattern": "Number of checking attempts must be a positive integer.",
"number-of-checking-attempts-required": "Number of checking attempts is required.", "number-of-checking-attempts-required": "Number of checking attempts is required.",
"number-of-send-attempts": "Number of send attempts",
"number-of-send-attempts-pattern": "Number of send attempts must be a positive integer.",
"number-of-send-attempts-required": "Number of send attempts is required.",
"provider": "Provider", "provider": "Provider",
"retry-verification-code-period": "Retry verification code period (sec)",
"retry-verification-code-period-pattern": "Minimal period time is 5 sec",
"retry-verification-code-period-required": "Retry verification code period is required.",
"total-allowed-time-for-verification": "Total allowed time for verification (sec)", "total-allowed-time-for-verification": "Total allowed time for verification (sec)",
"total-allowed-time-for-verification-pattern": "Total allowed time must be a positive integer.", "total-allowed-time-for-verification-pattern": "Total allowed time must be a positive integer.",
"total-allowed-time-for-verification-required": "Total allowed time is required.", "total-allowed-time-for-verification-required": "Total allowed time is required.",
@ -335,7 +335,6 @@
"verification-code-lifetime": "Verification code lifetime (sec)", "verification-code-lifetime": "Verification code lifetime (sec)",
"verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.", "verification-code-lifetime-pattern": "Verification code lifetime must be a positive integer.",
"verification-code-lifetime-required": "Verification code lifetime is required.", "verification-code-lifetime-required": "Verification code lifetime is required.",
"verification-code-send-rate-limit": "Verification code send rate limit",
"verification-message-template": "Verification message template", "verification-message-template": "Verification message template",
"verification-limitations": "Verification limitations", "verification-limitations": "Verification limitations",
"verification-message-template-pattern": "Verification message need to contains pattern: ${code}", "verification-message-template-pattern": "Verification message need to contains pattern: ${code}",
@ -2484,6 +2483,7 @@
"verify-your-identity": "Verify your identity", "verify-your-identity": "Verify your identity",
"select-way-to-verify": "Select a way to verify", "select-way-to-verify": "Select a way to verify",
"resend-code": "Resend code", "resend-code": "Resend code",
"resend-code-wait": "Resend code wait { time, plural, 1 {1 second} other {# seconds} }",
"try-another-way": "Try another way", "try-another-way": "Try another way",
"totp-auth-description": "Please enter the security code from your authenticator app.", "totp-auth-description": "Please enter the security code from your authenticator app.",
"sms-auth-description": "A security code has been sent to your phone at {{contact}}.", "sms-auth-description": "A security code has been sent to your phone at {{contact}}.",

Loading…
Cancel
Save