-
Notifications
You must be signed in to change notification settings - Fork 0
/
getToken.ts
112 lines (88 loc) · 3.19 KB
/
getToken.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import ms from 'ms'
import { JWT_AUDIENCE } from '../utils/enums'
import { State } from '../State'
import { createJwt } from '../utils/jwt'
import { ID, IUser } from '../types/interfaces'
/**
* return 10 "random" characters
*/
function getRandomChars(): string {
return Math.random().toString(36).substring(2, 10) // just 10 chars
}
function getRandomString(length: number): string {
let result = getRandomChars()
while (result.length < length) {
result = `${result}${getRandomChars()}`
}
if (result.length > length) {
result = result.substring(0, length)
}
return result
}
/**
* Password reset getToken method. This method should take approximately same time to execute regardless of input, to prevent timing attacks.
* If `passwordResetTokeRepository` is provided token is saved it this repository by calling `passwordResetTokeRepository.savePasswordResetToken`.
* This method should be used in the endpoint requesting password change. Token generated by this method should not be returned directly to user,
* but some other communication channel should be used (e.g. email)
* Returns reset password token encrypted with concatenation of server jwt secret and user password, which makes this token usable just once.
* @param email
*/
export default async function getToken(email: string): Promise<[string, IUser<ID>] | undefined> {
const state = State.getInstance()
let user = await state.userRepository.getUserByEmail(email, true)
const passportConfig = State.getInstance().config.passport
let passportSecret = passportConfig.jwt.secretOrKey
const randomString = getRandomString(60 + passportSecret.length)
// for preventing timing attacks
let mock = false
if (!user) {
mock = true
user = {
id: randomString.substring(0, 36),
hash: randomString.substring(0, 60)
}
passportSecret = randomString
}
const tokenPayload = {
uid: user.id
}
const expiresIn = passportConfig.jwt.passwordReset.exp
const tokenOptions = {
audience: JWT_AUDIENCE.PASSWORD_RESET,
expiresIn
}
const tokenSecret = `${passportSecret}${user.hash}`
const resetPasswordToken = await createJwt(tokenPayload, tokenOptions, tokenSecret)
if (mock) {
return undefined
}
// save token when savePasswordResetToken repository is provided
if (state.passwordResetTokenRepository) {
await state.passwordResetTokenRepository.savePasswordResetToken(user.id, resetPasswordToken, ms(expiresIn))
}
return [resetPasswordToken, user]
}
/*
// just for reference
export async function getTokenOld(email: string): Promise<undefined | string> {
const state = State.getInstance()
const user = await state.userRepository.getUserByEmail(email)
if (!user) {
return undefined
}
const tokenPayload = {
uid: user.id
}
const tokenOptions = {
audience: JWT_AUDIENCE.PASSWORD_RESET,
expiresIn: passportConfig.jwt.passwordReset.exp
}
const tokenSecret = `${passportConfig.jwt.secretOrKey}${user.hash}`
const resetPasswordToken = await createJwt(tokenPayload, tokenOptions, tokenSecret)
// save token when savePasswordResetToken repository is provided
if (state.passwordResetTokenRepository) {
await state.passwordResetTokenRepository.savePasswordResetToken(user.id, resetPasswordToken)
}
return resetPasswordToken
}
*/