aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-10-05 15:37:15 +0200
committerChocobozzz <me@florianbigard.com>2022-10-07 10:51:16 +0200
commit56f47830758ff8e92abcfcc5f35d474ab12fe215 (patch)
tree854e57ec1b800d6ad740c8e42bee00cbd21e1724 /server/lib
parent7dd7ff4cebc290b09fe00d82046bb58e4e8a800d (diff)
downloadPeerTube-56f47830758ff8e92abcfcc5f35d474ab12fe215.tar.gz
PeerTube-56f47830758ff8e92abcfcc5f35d474ab12fe215.tar.zst
PeerTube-56f47830758ff8e92abcfcc5f35d474ab12fe215.zip
Support two factor authentication in backend
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/auth/oauth.ts27
-rw-r--r--server/lib/redis.ts25
2 files changed, 48 insertions, 4 deletions
diff --git a/server/lib/auth/oauth.ts b/server/lib/auth/oauth.ts
index fa1887315..b541142a5 100644
--- a/server/lib/auth/oauth.ts
+++ b/server/lib/auth/oauth.ts
@@ -11,8 +11,20 @@ import OAuth2Server, {
11import { randomBytesPromise } from '@server/helpers/core-utils' 11import { randomBytesPromise } from '@server/helpers/core-utils'
12import { MOAuthClient } from '@server/types/models' 12import { MOAuthClient } from '@server/types/models'
13import { sha1 } from '@shared/extra-utils' 13import { sha1 } from '@shared/extra-utils'
14import { OAUTH_LIFETIME } from '../../initializers/constants' 14import { HttpStatusCode } from '@shared/models'
15import { OAUTH_LIFETIME, OTP } from '../../initializers/constants'
15import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model' 16import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model'
17import { isOTPValid } from '@server/helpers/otp'
18
19class MissingTwoFactorError extends Error {
20 code = HttpStatusCode.UNAUTHORIZED_401
21 name = 'missing_two_factor'
22}
23
24class InvalidTwoFactorError extends Error {
25 code = HttpStatusCode.BAD_REQUEST_400
26 name = 'invalid_two_factor'
27}
16 28
17/** 29/**
18 * 30 *
@@ -94,6 +106,9 @@ function handleOAuthAuthenticate (
94} 106}
95 107
96export { 108export {
109 MissingTwoFactorError,
110 InvalidTwoFactorError,
111
97 handleOAuthToken, 112 handleOAuthToken,
98 handleOAuthAuthenticate 113 handleOAuthAuthenticate
99} 114}
@@ -118,6 +133,16 @@ async function handlePasswordGrant (options: {
118 const user = await getUser(request.body.username, request.body.password, bypassLogin) 133 const user = await getUser(request.body.username, request.body.password, bypassLogin)
119 if (!user) throw new InvalidGrantError('Invalid grant: user credentials are invalid') 134 if (!user) throw new InvalidGrantError('Invalid grant: user credentials are invalid')
120 135
136 if (user.otpSecret) {
137 if (!request.headers[OTP.HEADER_NAME]) {
138 throw new MissingTwoFactorError('Missing two factor header')
139 }
140
141 if (isOTPValid({ secret: user.otpSecret, token: request.headers[OTP.HEADER_NAME] }) !== true) {
142 throw new InvalidTwoFactorError('Invalid two factor header')
143 }
144 }
145
121 const token = await buildToken() 146 const token = await buildToken()
122 147
123 return saveToken(token, client, user, { bypassLogin }) 148 return saveToken(token, client, user, { bypassLogin })
diff --git a/server/lib/redis.ts b/server/lib/redis.ts
index 9b3c72300..b7523492a 100644
--- a/server/lib/redis.ts
+++ b/server/lib/redis.ts
@@ -9,6 +9,7 @@ import {
9 CONTACT_FORM_LIFETIME, 9 CONTACT_FORM_LIFETIME,
10 RESUMABLE_UPLOAD_SESSION_LIFETIME, 10 RESUMABLE_UPLOAD_SESSION_LIFETIME,
11 TRACKER_RATE_LIMITS, 11 TRACKER_RATE_LIMITS,
12 TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME,
12 USER_EMAIL_VERIFY_LIFETIME, 13 USER_EMAIL_VERIFY_LIFETIME,
13 USER_PASSWORD_CREATE_LIFETIME, 14 USER_PASSWORD_CREATE_LIFETIME,
14 USER_PASSWORD_RESET_LIFETIME, 15 USER_PASSWORD_RESET_LIFETIME,
@@ -108,10 +109,24 @@ class Redis {
108 return this.removeValue(this.generateResetPasswordKey(userId)) 109 return this.removeValue(this.generateResetPasswordKey(userId))
109 } 110 }
110 111
111 async getResetPasswordLink (userId: number) { 112 async getResetPasswordVerificationString (userId: number) {
112 return this.getValue(this.generateResetPasswordKey(userId)) 113 return this.getValue(this.generateResetPasswordKey(userId))
113 } 114 }
114 115
116 /* ************ Two factor auth request ************ */
117
118 async setTwoFactorRequest (userId: number, otpSecret: string) {
119 const requestToken = await generateRandomString(32)
120
121 await this.setValue(this.generateTwoFactorRequestKey(userId, requestToken), otpSecret, TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME)
122
123 return requestToken
124 }
125
126 async getTwoFactorRequestToken (userId: number, requestToken: string) {
127 return this.getValue(this.generateTwoFactorRequestKey(userId, requestToken))
128 }
129
115 /* ************ Email verification ************ */ 130 /* ************ Email verification ************ */
116 131
117 async setVerifyEmailVerificationString (userId: number) { 132 async setVerifyEmailVerificationString (userId: number) {
@@ -342,6 +357,10 @@ class Redis {
342 return 'reset-password-' + userId 357 return 'reset-password-' + userId
343 } 358 }
344 359
360 private generateTwoFactorRequestKey (userId: number, token: string) {
361 return 'two-factor-request-' + userId + '-' + token
362 }
363
345 private generateVerifyEmailKey (userId: number) { 364 private generateVerifyEmailKey (userId: number) {
346 return 'verify-email-' + userId 365 return 'verify-email-' + userId
347 } 366 }
@@ -391,8 +410,8 @@ class Redis {
391 return JSON.parse(value) 410 return JSON.parse(value)
392 } 411 }
393 412
394 private setObject (key: string, value: { [ id: string ]: number | string }) { 413 private setObject (key: string, value: { [ id: string ]: number | string }, expirationMilliseconds?: number) {
395 return this.setValue(key, JSON.stringify(value)) 414 return this.setValue(key, JSON.stringify(value), expirationMilliseconds)
396 } 415 }
397 416
398 private async setValue (key: string, value: string, expirationMilliseconds?: number) { 417 private async setValue (key: string, value: string, expirationMilliseconds?: number) {