aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-account/my-account-settings/my-account-two-factor
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+my-account/my-account-settings/my-account-two-factor')
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts2
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html12
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts49
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html54
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss16
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts105
6 files changed, 238 insertions, 0 deletions
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts
new file mode 100644
index 000000000..cc774bde3
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/index.ts
@@ -0,0 +1,2 @@
1export * from './my-account-two-factor-button.component'
2export * from './my-account-two-factor.component'
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html
new file mode 100644
index 000000000..2fcfffbf3
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.html
@@ -0,0 +1,12 @@
1<div class="two-factor">
2 <ng-container *ngIf="!twoFactorEnabled">
3 <p i18n>Two factor authentication adds an additional layer of security to your account by requiring a numeric code from another device (most commonly mobile phones) when you log in.</p>
4
5 <my-button [routerLink]="[ '/my-account/two-factor-auth' ]" className="orange-button-link" i18n>Enable two-factor authentication</my-button>
6 </ng-container>
7
8 <ng-container *ngIf="twoFactorEnabled">
9 <my-button className="orange-button" (click)="disableTwoFactor()" i18n>Disable two-factor authentication</my-button>
10 </ng-container>
11
12</div>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts
new file mode 100644
index 000000000..97ffb6013
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor-button.component.ts
@@ -0,0 +1,49 @@
1import { Subject } from 'rxjs'
2import { Component, Input, OnInit } from '@angular/core'
3import { AuthService, ConfirmService, Notifier, User } from '@app/core'
4import { TwoFactorService } from '@app/shared/shared-users'
5
6@Component({
7 selector: 'my-account-two-factor-button',
8 templateUrl: './my-account-two-factor-button.component.html'
9})
10export class MyAccountTwoFactorButtonComponent implements OnInit {
11 @Input() user: User = null
12 @Input() userInformationLoaded: Subject<any>
13
14 twoFactorEnabled = false
15
16 constructor (
17 private notifier: Notifier,
18 private twoFactorService: TwoFactorService,
19 private confirmService: ConfirmService,
20 private auth: AuthService
21 ) {
22 }
23
24 ngOnInit () {
25 this.userInformationLoaded.subscribe(() => {
26 this.twoFactorEnabled = this.user.twoFactorEnabled
27 })
28 }
29
30 async disableTwoFactor () {
31 const message = $localize`Are you sure you want to disable two factor authentication of your account?`
32
33 const { confirmed, password } = await this.confirmService.confirmWithPassword(message, $localize`Disable two factor`)
34 if (confirmed === false) return
35
36 this.twoFactorService.disableTwoFactor({ userId: this.user.id, currentPassword: password })
37 .subscribe({
38 next: () => {
39 this.twoFactorEnabled = false
40
41 this.auth.refreshUserInformation()
42
43 this.notifier.success($localize`Two factor authentication disabled`)
44 },
45
46 error: err => this.notifier.error(err.message)
47 })
48 }
49}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html
new file mode 100644
index 000000000..16c344e3b
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.html
@@ -0,0 +1,54 @@
1<h1>
2 <my-global-icon iconName="cog" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Two factor authentication</ng-container>
4</h1>
5
6<div i18n *ngIf="twoFactorAlreadyEnabled === true" class="root already-enabled">
7 Two factor authentication is already enabled.
8</div>
9
10<div class="root" *ngIf="twoFactorAlreadyEnabled === false">
11 <ng-container *ngIf="step === 'request'">
12 <form role="form" (ngSubmit)="requestTwoFactor()" [formGroup]="formPassword">
13
14 <label i18n for="current-password">Your password</label>
15 <div class="form-group-description" i18n>Confirm your password to enable two factor authentication</div>
16
17 <my-input-text
18 formControlName="current-password" inputId="current-password" i18n-placeholder placeholder="Current password"
19 [formError]="formErrorsPassword['current-password']" autocomplete="current-password"
20 ></my-input-text>
21
22 <input class="peertube-button orange-button mt-3" type="submit" i18n-value value="Confirm" [disabled]="!formPassword.valid">
23 </form>
24 </ng-container>
25
26 <ng-container *ngIf="step === 'confirm'">
27
28 <p i18n>
29 Scan this QR code into a TOTP app on your phone. This app will generate tokens that you will have to enter when logging in.
30 </p>
31
32 <qrcode [qrdata]="twoFactorURI" [width]="256" level="Q"></qrcode>
33
34 <div i18n>
35 If you can't scan the QR code and need to enter it manually, here is the plain-text secret:
36 </div>
37
38 <div class="secret-plain-text">{{ twoFactorSecret }}</div>
39
40 <form class="mt-3" role="form" (ngSubmit)="confirmTwoFactor()" [formGroup]="formOTP">
41
42 <label i18n for="otp-token">Two-factor code</label>
43 <div class="form-group-description" i18n>Enter the code generated by your authenticator app to confirm</div>
44
45 <my-input-text
46 [show]="true" formControlName="otp-token" inputId="otp-token"
47 [formError]="formErrorsOTP['otp-token']" autocomplete="otp-token"
48 ></my-input-text>
49
50 <input class="peertube-button orange-button mt-3" type="submit" i18n-value value="Confirm" [disabled]="!formOTP.valid">
51 </form>
52 </ng-container>
53
54</div>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss
new file mode 100644
index 000000000..cee016bb8
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.scss
@@ -0,0 +1,16 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3
4.root {
5 max-width: 600px;
6}
7
8.secret-plain-text {
9 font-family: monospace;
10 font-size: 0.9rem;
11}
12
13qrcode {
14 display: inline-block;
15 margin: auto;
16}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts
new file mode 100644
index 000000000..259090d64
--- /dev/null
+++ b/client/src/app/+my-account/my-account-settings/my-account-two-factor/my-account-two-factor.component.ts
@@ -0,0 +1,105 @@
1import { Component, OnInit } from '@angular/core'
2import { FormGroup } from '@angular/forms'
3import { Router } from '@angular/router'
4import { AuthService, Notifier, User } from '@app/core'
5import { USER_EXISTING_PASSWORD_VALIDATOR, USER_OTP_TOKEN_VALIDATOR } from '@app/shared/form-validators/user-validators'
6import { FormReactiveService } from '@app/shared/shared-forms'
7import { TwoFactorService } from '@app/shared/shared-users'
8
9@Component({
10 selector: 'my-account-two-factor',
11 templateUrl: './my-account-two-factor.component.html',
12 styleUrls: [ './my-account-two-factor.component.scss' ]
13})
14export class MyAccountTwoFactorComponent implements OnInit {
15 twoFactorAlreadyEnabled: boolean
16
17 step: 'request' | 'confirm' | 'confirmed' = 'request'
18
19 twoFactorSecret: string
20 twoFactorURI: string
21
22 inPasswordStep = true
23
24 formPassword: FormGroup
25 formErrorsPassword: any
26
27 formOTP: FormGroup
28 formErrorsOTP: any
29
30 private user: User
31 private requestToken: string
32
33 constructor (
34 private notifier: Notifier,
35 private twoFactorService: TwoFactorService,
36 private formReactiveService: FormReactiveService,
37 private auth: AuthService,
38 private router: Router
39 ) {
40 }
41
42 ngOnInit () {
43 this.buildPasswordForm()
44 this.buildOTPForm()
45
46 this.auth.userInformationLoaded.subscribe(() => {
47 this.user = this.auth.getUser()
48
49 this.twoFactorAlreadyEnabled = this.user.twoFactorEnabled
50 })
51 }
52
53 requestTwoFactor () {
54 this.twoFactorService.requestTwoFactor({
55 userId: this.user.id,
56 currentPassword: this.formPassword.value['current-password']
57 }).subscribe({
58 next: ({ otpRequest }) => {
59 this.requestToken = otpRequest.requestToken
60 this.twoFactorURI = otpRequest.uri
61 this.twoFactorSecret = otpRequest.secret.replace(/(.{4})/g, '$1 ').trim()
62
63 this.step = 'confirm'
64 },
65
66 error: err => this.notifier.error(err.message)
67 })
68 }
69
70 confirmTwoFactor () {
71 this.twoFactorService.confirmTwoFactorRequest({
72 userId: this.user.id,
73 requestToken: this.requestToken,
74 otpToken: this.formOTP.value['otp-token']
75 }).subscribe({
76 next: () => {
77 this.notifier.success($localize`Two factor authentication has been enabled.`)
78
79 this.auth.refreshUserInformation()
80
81 this.router.navigateByUrl('/my-account/settings')
82 },
83
84 error: err => this.notifier.error(err.message)
85 })
86 }
87
88 private buildPasswordForm () {
89 const { form, formErrors } = this.formReactiveService.buildForm({
90 'current-password': USER_EXISTING_PASSWORD_VALIDATOR
91 })
92
93 this.formPassword = form
94 this.formErrorsPassword = formErrors
95 }
96
97 private buildOTPForm () {
98 const { form, formErrors } = this.formReactiveService.buildForm({
99 'otp-token': USER_OTP_TOKEN_VALIDATOR
100 })
101
102 this.formOTP = form
103 this.formErrorsOTP = formErrors
104 }
105}