aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+login
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+login')
-rw-r--r--client/src/app/+login/login.component.html46
-rw-r--r--client/src/app/+login/login.component.scss4
-rw-r--r--client/src/app/+login/login.component.ts41
3 files changed, 65 insertions, 26 deletions
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html
index f3a2476f9..49b443a20 100644
--- a/client/src/app/+login/login.component.html
+++ b/client/src/app/+login/login.component.html
@@ -39,34 +39,48 @@
39 <div class="login-form-and-externals"> 39 <div class="login-form-and-externals">
40 40
41 <form myPluginSelector pluginSelectorId="login-form" role="form" (ngSubmit)="login()" [formGroup]="form"> 41 <form myPluginSelector pluginSelectorId="login-form" role="form" (ngSubmit)="login()" [formGroup]="form">
42 <div class="form-group"> 42 <ng-container *ngIf="!otpStep">
43 <div> 43 <div class="form-group">
44 <label i18n for="username">Username or email address</label> 44 <div>
45 <input 45 <label i18n for="username">Username or email address</label>
46 type="text" id="username" i18n-placeholder placeholder="Example: john@example.com" required tabindex="1" 46 <input
47 formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus 47 type="text" id="username" i18n-placeholder placeholder="Example: john@example.com" required tabindex="1"
48 > 48 formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus
49 >
50 </div>
51
52 <div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div>
53
54 <div *ngIf="hasUsernameUppercase()" i18n class="form-warning">
55 ⚠️ Most email addresses do not include capital letters.
56 </div>
49 </div> 57 </div>
50 58
51 <div *ngIf="formErrors.username" class="form-error">{{ formErrors.username }}</div> 59 <div class="form-group">
60 <label i18n for="password">Password</label>
52 61
53 <div *ngIf="hasUsernameUppercase()" i18n class="form-warning"> 62 <my-input-text
54 ⚠️ Most email addresses do not include capital letters. 63 formControlName="password" inputId="password" i18n-placeholder placeholder="Password"
64 [formError]="formErrors['password']" autocomplete="current-password" [tabindex]="2"
65 ></my-input-text>
55 </div> 66 </div>
56 </div> 67 </ng-container>
68
69 <div *ngIf="otpStep" class="form-group">
70 <p i18n>Enter the two-factor code generated by your phone app:</p>
57 71
58 <div class="form-group"> 72 <label i18n for="otp-token">Two factor authentication token</label>
59 <label i18n for="password">Password</label>
60 73
61 <my-input-text 74 <my-input-text
62 formControlName="password" inputId="password" i18n-placeholder placeholder="Password" 75 #otpTokenInput
63 [formError]="formErrors['password']" autocomplete="current-password" [tabindex]="2" 76 [show]="true" formControlName="otp-token" inputId="otp-token"
77 [formError]="formErrors['otp-token']" autocomplete="otp-token"
64 ></my-input-text> 78 ></my-input-text>
65 </div> 79 </div>
66 80
67 <input type="submit" class="peertube-button orange-button" i18n-value value="Login" [disabled]="!form.valid"> 81 <input type="submit" class="peertube-button orange-button" i18n-value value="Login" [disabled]="!form.valid">
68 82
69 <div class="additional-links"> 83 <div *ngIf="!otpStep" class="additional-links">
70 <a i18n role="button" class="link-orange" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a> 84 <a i18n role="button" class="link-orange" (click)="openForgotPasswordModal()" i18n-title title="Click here to reset your password">I forgot my password</a>
71 85
72 <ng-container *ngIf="signupAllowed"> 86 <ng-container *ngIf="signupAllowed">
diff --git a/client/src/app/+login/login.component.scss b/client/src/app/+login/login.component.scss
index d31d428f7..17e151fd8 100644
--- a/client/src/app/+login/login.component.scss
+++ b/client/src/app/+login/login.component.scss
@@ -1,8 +1,8 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4@import '~bootstrap/scss/functions'; 4@import 'bootstrap/scss/functions';
5@import '~bootstrap/scss/variables'; 5@import 'bootstrap/scss/variables';
6 6
7label { 7label {
8 display: block; 8 display: block;
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index 2ed9be16c..c1705807f 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -1,10 +1,10 @@
1
2import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core' 1import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 2import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core' 3import { AuthService, Notifier, RedirectService, SessionStorageService, UserService } from '@app/core'
5import { HooksService } from '@app/core/plugins/hooks.service' 4import { HooksService } from '@app/core/plugins/hooks.service'
6import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators' 5import { LOGIN_PASSWORD_VALIDATOR, LOGIN_USERNAME_VALIDATOR } from '@app/shared/form-validators/login-validators'
7import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { USER_OTP_TOKEN_VALIDATOR } from '@app/shared/form-validators/user-validators'
7import { FormReactive, FormReactiveService, InputTextComponent } from '@app/shared/shared-forms'
8import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance' 8import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance'
9import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 9import { NgbAccordion, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
10import { PluginsManager } from '@root-helpers/plugins-manager' 10import { PluginsManager } from '@root-helpers/plugins-manager'
@@ -20,6 +20,7 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
20 private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url' 20 private static SESSION_STORAGE_REDIRECT_URL_KEY = 'login-previous-url'
21 21
22 @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef 22 @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef
23 @ViewChild('otpTokenInput') otpTokenInput: InputTextComponent
23 24
24 accordion: NgbAccordion 25 accordion: NgbAccordion
25 error: string = null 26 error: string = null
@@ -37,11 +38,13 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
37 codeOfConduct: false 38 codeOfConduct: false
38 } 39 }
39 40
41 otpStep = false
42
40 private openedForgotPasswordModal: NgbModalRef 43 private openedForgotPasswordModal: NgbModalRef
41 private serverConfig: ServerConfig 44 private serverConfig: ServerConfig
42 45
43 constructor ( 46 constructor (
44 protected formValidatorService: FormValidatorService, 47 protected formReactiveService: FormReactiveService,
45 private route: ActivatedRoute, 48 private route: ActivatedRoute,
46 private modalService: NgbModal, 49 private modalService: NgbModal,
47 private authService: AuthService, 50 private authService: AuthService,
@@ -82,7 +85,11 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
82 // Avoid undefined errors when accessing form error properties 85 // Avoid undefined errors when accessing form error properties
83 this.buildForm({ 86 this.buildForm({
84 username: LOGIN_USERNAME_VALIDATOR, 87 username: LOGIN_USERNAME_VALIDATOR,
85 password: LOGIN_PASSWORD_VALIDATOR 88 password: LOGIN_PASSWORD_VALIDATOR,
89 'otp-token': {
90 VALIDATORS: [], // Will be set dynamically
91 MESSAGES: USER_OTP_TOKEN_VALIDATOR.MESSAGES
92 }
86 }) 93 })
87 94
88 this.serverConfig = snapshot.data.serverConfig 95 this.serverConfig = snapshot.data.serverConfig
@@ -118,13 +125,20 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
118 login () { 125 login () {
119 this.error = null 126 this.error = null
120 127
121 const { username, password } = this.form.value 128 const options = {
129 username: this.form.value['username'],
130 password: this.form.value['password'],
131 otpToken: this.form.value['otp-token']
132 }
122 133
123 this.authService.login(username, password) 134 this.authService.login(options)
135 .pipe()
124 .subscribe({ 136 .subscribe({
125 next: () => this.redirectService.redirectToPreviousRoute(), 137 next: () => this.redirectService.redirectToPreviousRoute(),
126 138
127 error: err => this.handleError(err) 139 error: err => {
140 this.handleError(err)
141 }
128 }) 142 })
129 } 143 }
130 144
@@ -162,7 +176,7 @@ The link will expire within 1 hour.`
162 private loadExternalAuthToken (username: string, token: string) { 176 private loadExternalAuthToken (username: string, token: string) {
163 this.isAuthenticatedWithExternalAuth = true 177 this.isAuthenticatedWithExternalAuth = true
164 178
165 this.authService.login(username, null, token) 179 this.authService.login({ username, password: null, token })
166 .subscribe({ 180 .subscribe({
167 next: () => { 181 next: () => {
168 const redirectUrl = this.storage.getItem(LoginComponent.SESSION_STORAGE_REDIRECT_URL_KEY) 182 const redirectUrl = this.storage.getItem(LoginComponent.SESSION_STORAGE_REDIRECT_URL_KEY)
@@ -182,6 +196,17 @@ The link will expire within 1 hour.`
182 } 196 }
183 197
184 private handleError (err: any) { 198 private handleError (err: any) {
199 if (this.authService.isOTPMissingError(err)) {
200 this.otpStep = true
201
202 setTimeout(() => {
203 this.form.get('otp-token').setValidators(USER_OTP_TOKEN_VALIDATOR.VALIDATORS)
204 this.otpTokenInput.focus()
205 })
206
207 return
208 }
209
185 if (err.message.indexOf('credentials are invalid') !== -1) this.error = $localize`Incorrect username or password.` 210 if (err.message.indexOf('credentials are invalid') !== -1) this.error = $localize`Incorrect username or password.`
186 else if (err.message.indexOf('blocked') !== -1) this.error = $localize`Your account is blocked.` 211 else if (err.message.indexOf('blocked') !== -1) this.error = $localize`Your account is blocked.`
187 else this.error = err.message 212 else this.error = err.message