aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/core
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-10-07 11:06:28 +0200
committerChocobozzz <me@florianbigard.com>2022-10-07 11:06:28 +0200
commitd12b40fb96d56786a96c06a621f3d8e0a0d24f4a (patch)
tree7047fa5cd7e778eb377c897eccb539c52b2e59bc /client/src/app/core
parent56f47830758ff8e92abcfcc5f35d474ab12fe215 (diff)
downloadPeerTube-d12b40fb96d56786a96c06a621f3d8e0a0d24f4a.tar.gz
PeerTube-d12b40fb96d56786a96c06a621f3d8e0a0d24f4a.tar.zst
PeerTube-d12b40fb96d56786a96c06a621f3d8e0a0d24f4a.zip
Implement two factor in client
Diffstat (limited to 'client/src/app/core')
-rw-r--r--client/src/app/core/auth/auth.service.ts23
-rw-r--r--client/src/app/core/confirm/confirm.service.ts47
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts6
-rw-r--r--client/src/app/core/users/user.model.ts4
4 files changed, 64 insertions, 16 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index ca46866f5..7f4fae4aa 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -1,7 +1,7 @@
1import { Hotkey, HotkeysService } from 'angular2-hotkeys' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' 2import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs'
3import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' 3import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
4import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
@@ -141,7 +141,14 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
141 return !!this.getAccessToken() 141 return !!this.getAccessToken()
142 } 142 }
143 143
144 login (username: string, password: string, token?: string) { 144 login (options: {
145 username: string
146 password: string
147 otpToken?: string
148 token?: string
149 }) {
150 const { username, password, token, otpToken } = options
151
145 // Form url encoded 152 // Form url encoded
146 const body = { 153 const body = {
147 client_id: this.clientId, 154 client_id: this.clientId,
@@ -155,7 +162,9 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
155 162
156 if (token) Object.assign(body, { externalAuthToken: token }) 163 if (token) Object.assign(body, { externalAuthToken: token })
157 164
158 const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded') 165 let headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
166 if (otpToken) headers = headers.set('x-peertube-otp', otpToken)
167
159 return this.http.post<UserLogin>(AuthService.BASE_TOKEN_URL, objectToUrlEncoded(body), { headers }) 168 return this.http.post<UserLogin>(AuthService.BASE_TOKEN_URL, objectToUrlEncoded(body), { headers })
160 .pipe( 169 .pipe(
161 map(res => Object.assign(res, { username })), 170 map(res => Object.assign(res, { username })),
@@ -245,6 +254,14 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
245 }) 254 })
246 } 255 }
247 256
257 isOTPMissingError (err: HttpErrorResponse) {
258 if (err.status !== HttpStatusCode.UNAUTHORIZED_401) return false
259
260 if (err.headers.get('x-peertube-otp') !== 'required; app') return false
261
262 return true
263 }
264
248 private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> { 265 private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> {
249 // User is not loaded yet, set manually auth header 266 // User is not loaded yet, set manually auth header
250 const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`) 267 const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`)
diff --git a/client/src/app/core/confirm/confirm.service.ts b/client/src/app/core/confirm/confirm.service.ts
index 338b8762c..89a25f0a5 100644
--- a/client/src/app/core/confirm/confirm.service.ts
+++ b/client/src/app/core/confirm/confirm.service.ts
@@ -1,28 +1,53 @@
1import { firstValueFrom, Subject } from 'rxjs' 1import { firstValueFrom, map, Observable, Subject } from 'rxjs'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3 3
4type ConfirmOptions = { 4type ConfirmOptions = {
5 title: string 5 title: string
6 message: string 6 message: string
7 inputLabel?: string 7} & (
8 expectedInputValue?: string 8 {
9 confirmButtonText?: string 9 type: 'confirm'
10} 10 confirmButtonText?: string
11 } |
12 {
13 type: 'confirm-password'
14 confirmButtonText?: string
15 } |
16 {
17 type: 'confirm-expected-input'
18 inputLabel?: string
19 expectedInputValue?: string
20 confirmButtonText?: string
21 }
22)
11 23
12@Injectable() 24@Injectable()
13export class ConfirmService { 25export class ConfirmService {
14 showConfirm = new Subject<ConfirmOptions>() 26 showConfirm = new Subject<ConfirmOptions>()
15 confirmResponse = new Subject<boolean>() 27 confirmResponse = new Subject<{ confirmed: boolean, value?: string }>()
16 28
17 confirm (message: string, title = '', confirmButtonText?: string) { 29 confirm (message: string, title = '', confirmButtonText?: string) {
18 this.showConfirm.next({ title, message, confirmButtonText }) 30 this.showConfirm.next({ type: 'confirm', title, message, confirmButtonText })
31
32 return firstValueFrom(this.extractConfirmed(this.confirmResponse.asObservable()))
33 }
19 34
20 return firstValueFrom(this.confirmResponse.asObservable()) 35 confirmWithPassword (message: string, title = '', confirmButtonText?: string) {
36 this.showConfirm.next({ type: 'confirm-password', title, message, confirmButtonText })
37
38 const obs = this.confirmResponse.asObservable()
39 .pipe(map(({ confirmed, value }) => ({ confirmed, password: value })))
40
41 return firstValueFrom(obs)
21 } 42 }
22 43
23 confirmWithInput (message: string, inputLabel: string, expectedInputValue: string, title = '', confirmButtonText?: string) { 44 confirmWithExpectedInput (message: string, inputLabel: string, expectedInputValue: string, title = '', confirmButtonText?: string) {
24 this.showConfirm.next({ title, message, inputLabel, expectedInputValue, confirmButtonText }) 45 this.showConfirm.next({ type: 'confirm-expected-input', title, message, inputLabel, expectedInputValue, confirmButtonText })
46
47 return firstValueFrom(this.extractConfirmed(this.confirmResponse.asObservable()))
48 }
25 49
26 return firstValueFrom(this.confirmResponse.asObservable()) 50 private extractConfirmed (obs: Observable<{ confirmed: boolean }>) {
51 return obs.pipe(map(({ confirmed }) => confirmed))
27 } 52 }
28} 53}
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 7eec2eca6..57dd9ae26 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -4,6 +4,7 @@ import { Router } from '@angular/router'
4import { DateFormat, dateToHuman } from '@app/helpers' 4import { DateFormat, dateToHuman } from '@app/helpers'
5import { logger } from '@root-helpers/logger' 5import { logger } from '@root-helpers/logger'
6import { HttpStatusCode, ResultList } from '@shared/models' 6import { HttpStatusCode, ResultList } from '@shared/models'
7import { HttpHeaderResponse } from '@angular/common/http'
7 8
8@Injectable() 9@Injectable()
9export class RestExtractor { 10export class RestExtractor {
@@ -54,10 +55,11 @@ export class RestExtractor {
54 handleError (err: any) { 55 handleError (err: any) {
55 const errorMessage = this.buildErrorMessage(err) 56 const errorMessage = this.buildErrorMessage(err)
56 57
57 const errorObj: { message: string, status: string, body: string } = { 58 const errorObj: { message: string, status: string, body: string, headers: HttpHeaderResponse } = {
58 message: errorMessage, 59 message: errorMessage,
59 status: undefined, 60 status: undefined,
60 body: undefined 61 body: undefined,
62 headers: err.headers
61 } 63 }
62 64
63 if (err.status) { 65 if (err.status) {
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts
index 6ba30e4b8..8385a4012 100644
--- a/client/src/app/core/users/user.model.ts
+++ b/client/src/app/core/users/user.model.ts
@@ -66,6 +66,8 @@ export class User implements UserServerModel {
66 66
67 lastLoginDate: Date | null 67 lastLoginDate: Date | null
68 68
69 twoFactorEnabled: boolean
70
69 createdAt: Date 71 createdAt: Date
70 72
71 constructor (hash: Partial<UserServerModel>) { 73 constructor (hash: Partial<UserServerModel>) {
@@ -108,6 +110,8 @@ export class User implements UserServerModel {
108 110
109 this.notificationSettings = hash.notificationSettings 111 this.notificationSettings = hash.notificationSettings
110 112
113 this.twoFactorEnabled = hash.twoFactorEnabled
114
111 this.createdAt = hash.createdAt 115 this.createdAt = hash.createdAt
112 116
113 this.pluginAuth = hash.pluginAuth 117 this.pluginAuth = hash.pluginAuth