1
+ import { promisify } from 'util'
1
2
import { PrismaClient , User } from '@prisma/client'
2
3
import * as bcrypt from 'bcryptjs'
3
4
import { injectable } from 'tsyringe'
5
+ import { v4 as generateToken } from 'uuid'
6
+ import { RedisClient } from 'redis'
7
+ import { sendEmail } from '../utils/sendEmail'
8
+ import { resetPasswordTemplate } from '../utils/resetPasswordTemplate'
4
9
5
10
export interface AuthenticationMessage {
6
11
message ?: string
@@ -13,7 +18,7 @@ export interface AuthenticateReturn {
13
18
14
19
@injectable ( )
15
20
export class AuthService {
16
- constructor ( private prisma : PrismaClient ) { }
21
+ constructor ( private prisma : PrismaClient , private redisClient : RedisClient ) { }
17
22
18
23
async validateUser ( email : string , password : string ) : Promise < User | null > {
19
24
const user = await this . prisma . user . findUnique ( {
@@ -33,6 +38,27 @@ export class AuthService {
33
38
}
34
39
}
35
40
41
+ async resetPassword ( email : string ) : Promise < boolean > {
42
+ const user = await this . getUserByEmail ( email )
43
+ if ( ! user ) {
44
+ return true
45
+ }
46
+ const PREFIX = process . env . PREFIX || 'forgot-password'
47
+ const key = PREFIX + user . id
48
+ const token = generateToken ( )
49
+ const hashedToken = await this . hashString ( token )
50
+ this . redisClient . set ( key , hashedToken , 'ex' , 1000 * 60 * 60 * 24 ) // VALID FOR ONE DAY
51
+ await sendEmail ( email , resetPasswordTemplate ( user . id , token ) )
52
+ return true
53
+ }
54
+
55
+ async hashString ( password : string ) : Promise < string > {
56
+ // Hash password before saving in database
57
+ // We use salted hashing to prevent rainbow table attacks
58
+ const salt = await bcrypt . genSalt ( )
59
+ return await bcrypt . hash ( password , salt )
60
+ }
61
+
36
62
async getUserById ( id : string ) : Promise < User | null > {
37
63
return await this . prisma . user . findUnique ( {
38
64
where : {
@@ -59,11 +85,7 @@ export class AuthService {
59
85
if ( userWithEmailAlreadyExists ) {
60
86
throw new Error ( `User with email '${ email } ' already exists.` )
61
87
}
62
-
63
- // Hash password before saving in database
64
- // We use salted hashing to prevent rainbow table attacks
65
- const salt = await bcrypt . genSalt ( )
66
- const hashedPassword = await bcrypt . hash ( password , salt )
88
+ const hashedPassword = await this . hashString ( password )
67
89
68
90
return await this . prisma . user . create ( {
69
91
data : {
@@ -72,4 +94,35 @@ export class AuthService {
72
94
} ,
73
95
} )
74
96
}
97
+
98
+ async updatePassword (
99
+ token : string ,
100
+ id : string ,
101
+ newPassword : string
102
+ ) : Promise < User | null > {
103
+ if ( newPassword . length <= 6 ) {
104
+ return null
105
+ }
106
+ const PREFIX = process . env . PREFIX || 'forgot-password'
107
+ const key = PREFIX + id
108
+ const getAsync = promisify ( this . redisClient . get ) . bind ( this . redisClient )
109
+ const hashedToken = await getAsync ( key )
110
+ if ( ! hashedToken ) {
111
+ return null
112
+ }
113
+ const checkToken = await bcrypt . compare ( token , hashedToken )
114
+ if ( checkToken ) {
115
+ this . redisClient . del ( key )
116
+ const hashedPassword = await this . hashString ( newPassword )
117
+ return await this . prisma . user . update ( {
118
+ where : {
119
+ id,
120
+ } ,
121
+ data : {
122
+ password : hashedPassword ,
123
+ } ,
124
+ } )
125
+ }
126
+ return null
127
+ }
75
128
}
0 commit comments