1
1
const LocalStrategy = require ( 'passport-local' ) . Strategy ;
2
2
const bcrypt = require ( 'bcrypt' ) ;
3
+ const crypto = require ( 'crypto' ) ;
3
4
const {
4
5
getByUsername,
5
6
getByEmail,
6
7
create,
8
+ updateById,
9
+ findByVerificationToken,
10
+ refreshVerificationToken,
7
11
} = require ( '../domains/user/service' ) ;
8
12
const { AppError } = require ( '../libraries/error-handling/AppError' ) ;
13
+ const { sendVerificationEmail } = require ( '../libraries/email/emailService' ) ;
9
14
10
15
const verifyCallback = async ( username , password , done ) => {
11
16
try {
12
17
// Find user by email (since we're using email as username)
13
18
const user = await getByEmail ( username ) ;
14
-
19
+
15
20
if ( ! user ) {
16
21
return done ( null , false , { message : 'Incorrect email.' } ) ;
17
22
}
18
23
19
24
// Verify this is a local auth user
20
25
if ( user . authType !== 'local' ) {
21
- return done ( null , false , {
22
- message : `Please use ${ user . authType } authentication for this account.`
26
+ return done ( null , false , {
27
+ message : `Please use ${ user . authType } authentication for this account.`
23
28
} ) ;
24
29
}
25
30
31
+ // Check if email is verified
32
+ if ( ! user . isVerified ) {
33
+ return done ( null , false , { message : 'Please verify your email address before signing in.' , reason : 'email-not-verified' } ) ;
34
+ }
35
+
26
36
// Verify password
27
37
const isValidPassword = await bcrypt . compare ( password , user . local . password ) ;
28
38
if ( ! isValidPassword ) {
@@ -40,6 +50,10 @@ const verifyCallback = async (username, password, done) => {
40
50
}
41
51
} ;
42
52
53
+ const generateVerificationToken = ( ) => {
54
+ return crypto . randomBytes ( 32 ) . toString ( 'hex' ) ;
55
+ } ;
56
+
43
57
const registerUser = async ( { email, password } ) => {
44
58
try {
45
59
// Check if user already exists
@@ -52,23 +66,32 @@ const registerUser = async ({ email, password }) => {
52
66
const salt = await bcrypt . genSalt ( 10 ) ;
53
67
const hashedPassword = await bcrypt . hash ( password , salt ) ;
54
68
69
+ // Generate verification token
70
+ const verificationToken = generateVerificationToken ( ) ;
71
+ const verificationTokenExpiry = new Date ( Date . now ( ) + 24 * 60 * 60 * 1000 ) ; // 24 hours from now
72
+
55
73
// Create user payload matching new schema structure
56
74
const payload = {
57
75
email,
58
76
displayName : email ,
59
- authType : 'local' , // Specify auth type
77
+ authType : 'local' ,
60
78
local : {
61
79
username : email ,
62
80
password : hashedPassword ,
63
81
} ,
64
82
isDemo : false ,
65
83
isVerified : false ,
66
84
isAdmin : false ,
85
+ verificationToken,
86
+ verificationTokenExpiry,
67
87
} ;
68
88
69
89
// Create the user
70
90
const newUser = await create ( payload ) ;
71
91
92
+ // Send verification email
93
+ await sendVerificationEmail ( email , verificationToken ) ;
94
+
72
95
// Prepare the user object for the session
73
96
const userObj = newUser . toObject ( ) ;
74
97
const trimmedPayloadForSession = {
@@ -87,6 +110,55 @@ const registerUser = async ({ email, password }) => {
87
110
}
88
111
} ;
89
112
113
+ const verifyEmail = async ( token ) => {
114
+ try {
115
+ const user = await findByVerificationToken ( token ) ;
116
+
117
+ if ( ! user ) {
118
+ throw new AppError ( 'invalid-token' , 'Invalid or expired verification token' , 400 ) ;
119
+ }
120
+
121
+ // Update user as verified
122
+ await updateById ( user . _id , {
123
+ isVerified : true ,
124
+ verificationToken : null ,
125
+ verificationTokenExpiry : null ,
126
+ updatedAt : new Date ( )
127
+ } ) ;
128
+
129
+ return { message : 'Email verified successfully' } ;
130
+ } catch ( error ) {
131
+ if ( error instanceof AppError ) {
132
+ throw error ;
133
+ }
134
+ throw new AppError ( 'verification-failed' , error . message , 400 ) ;
135
+ }
136
+ } ;
137
+
138
+ const resendVerificationEmail = async ( email ) => {
139
+ try {
140
+ const { user, verificationToken } = await refreshVerificationToken ( email ) ;
141
+
142
+ // Send new verification email
143
+ await sendVerificationEmail ( email , verificationToken ) ;
144
+
145
+ return {
146
+ message : 'Verification email sent successfully' ,
147
+ email : user . email
148
+ } ;
149
+ } catch ( error ) {
150
+ if ( error instanceof AppError ) {
151
+ throw error ;
152
+ }
153
+ throw new AppError ( 'resend-verification-failed' , error . message , 400 ) ;
154
+ }
155
+ } ;
156
+
90
157
const localStrategy = new LocalStrategy ( verifyCallback ) ;
91
158
92
- module . exports = { localStrategy, registerUser } ;
159
+ module . exports = {
160
+ localStrategy,
161
+ registerUser,
162
+ verifyEmail,
163
+ resendVerificationEmail,
164
+ } ;
0 commit comments