@@ -5,7 +5,6 @@ const crypto = require("crypto");
5
5
const sendEmail = require ( '_helpers/send-email' ) ;
6
6
const db = require ( '_helpers/db' ) ;
7
7
const Role = require ( '_helpers/role' ) ;
8
- const Account = db . Account ;
9
8
10
9
module . exports = {
11
10
authenticate,
@@ -24,19 +23,18 @@ module.exports = {
24
23
} ;
25
24
26
25
async function authenticate ( { email, password, ipAddress } ) {
27
- const account = await Account . findOne ( { email } ) ;
26
+ const account = await db . Account . findOne ( { email } ) ;
28
27
29
28
if ( ! account || ! account . isVerified || ! bcrypt . compareSync ( password , account . passwordHash ) ) {
30
29
throw 'Email or password is incorrect' ;
31
30
}
32
31
33
32
// authentication successful so generate jwt and refresh tokens
34
33
const jwtToken = generateJwtToken ( account ) ;
35
- const refreshToken = generateRefreshToken ( ipAddress ) ;
34
+ const refreshToken = generateRefreshToken ( account , ipAddress ) ;
36
35
37
36
// save refresh token
38
- account . refreshTokens . push ( refreshToken ) ;
39
- account . save ( ) ;
37
+ await refreshToken . save ( ) ;
40
38
41
39
// return basic details and tokens
42
40
return {
@@ -47,15 +45,16 @@ async function authenticate({ email, password, ipAddress }) {
47
45
}
48
46
49
47
async function refreshToken ( { token, ipAddress } ) {
50
- const { account, refreshToken } = await getRefreshToken ( token ) ;
48
+ const refreshToken = await getRefreshToken ( token ) ;
49
+ const { account } = refreshToken ;
51
50
52
51
// replace old refresh token with a new one and save
53
- const newRefreshToken = generateRefreshToken ( ipAddress ) ;
52
+ const newRefreshToken = generateRefreshToken ( account , ipAddress ) ;
54
53
refreshToken . revoked = Date . now ( ) ;
55
54
refreshToken . revokedByIp = ipAddress ;
56
55
refreshToken . replacedByToken = newRefreshToken . token ;
57
- account . refreshTokens . push ( newRefreshToken ) ;
58
- account . save ( ) ;
56
+ await refreshToken . save ( ) ;
57
+ await newRefreshToken . save ( ) ;
59
58
60
59
// generate new jwt
61
60
const jwtToken = generateJwtToken ( account ) ;
@@ -69,26 +68,26 @@ async function refreshToken({ token, ipAddress }) {
69
68
}
70
69
71
70
async function revokeToken ( { token, ipAddress } ) {
72
- const { account , refreshToken } = await getRefreshToken ( token ) ;
71
+ const refreshToken = await getRefreshToken ( token ) ;
73
72
74
73
// revoke token and save
75
74
refreshToken . revoked = Date . now ( ) ;
76
75
refreshToken . revokedByIp = ipAddress ;
77
- account . save ( )
76
+ await refreshToken . save ( ) ;
78
77
}
79
78
80
79
async function register ( params , origin ) {
81
80
// validate
82
- if ( await Account . findOne ( { email : params . email } ) ) {
81
+ if ( await db . Account . findOne ( { email : params . email } ) ) {
83
82
// send already registered error in email to prevent account enumeration
84
- return sendAlreadyRegisteredEmail ( params . email , origin ) ;
83
+ return await sendAlreadyRegisteredEmail ( params . email , origin ) ;
85
84
}
86
85
87
86
// create account object
88
- const account = new Account ( params ) ;
87
+ const account = new db . Account ( params ) ;
89
88
90
89
// first registered account is an admin
91
- const isFirstAccount = ( await Account . countDocuments ( { } ) ) === 0 ;
90
+ const isFirstAccount = ( await db . Account . countDocuments ( { } ) ) === 0 ;
92
91
account . role = isFirstAccount ? Role . Admin : Role . User ;
93
92
account . verificationToken = randomTokenString ( ) ;
94
93
@@ -101,11 +100,11 @@ async function register(params, origin) {
101
100
await account . save ( ) ;
102
101
103
102
// send email
104
- sendVerificationEmail ( account , origin ) ;
103
+ await sendVerificationEmail ( account , origin ) ;
105
104
}
106
105
107
106
async function verifyEmail ( { token } ) {
108
- const account = await Account . findOne ( { verificationToken : token } ) ;
107
+ const account = await db . Account . findOne ( { verificationToken : token } ) ;
109
108
110
109
if ( ! account ) throw 'Verification failed' ;
111
110
@@ -115,7 +114,7 @@ async function verifyEmail({ token }) {
115
114
}
116
115
117
116
async function forgotPassword ( { email } , origin ) {
118
- const account = await Account . findOne ( { email } ) ;
117
+ const account = await db . Account . findOne ( { email } ) ;
119
118
120
119
// always return ok response to prevent email enumeration
121
120
if ( ! account ) return ;
@@ -125,14 +124,14 @@ async function forgotPassword({ email }, origin) {
125
124
token : randomTokenString ( ) ,
126
125
expires : new Date ( Date . now ( ) + 24 * 60 * 60 * 1000 )
127
126
} ;
128
- account . save ( ) ;
127
+ await account . save ( ) ;
129
128
130
129
// send email
131
- sendPasswordResetEmail ( account , origin ) ;
130
+ await sendPasswordResetEmail ( account , origin ) ;
132
131
}
133
132
134
133
async function validateResetToken ( { token } ) {
135
- const account = await Account . findOne ( {
134
+ const account = await db . Account . findOne ( {
136
135
'resetToken.token' : token ,
137
136
'resetToken.expires' : { $gt : Date . now ( ) }
138
137
} ) ;
@@ -141,7 +140,7 @@ async function validateResetToken({ token }) {
141
140
}
142
141
143
142
async function resetPassword ( { token, password } ) {
144
- const account = await Account . findOne ( {
143
+ const account = await db . Account . findOne ( {
145
144
'resetToken.token' : token ,
146
145
'resetToken.expires' : { $gt : Date . now ( ) }
147
146
} ) ;
@@ -156,7 +155,7 @@ async function resetPassword({ token, password }) {
156
155
}
157
156
158
157
async function getAll ( ) {
159
- const accounts = await Account . find ( ) ;
158
+ const accounts = await db . Account . find ( ) ;
160
159
return accounts . map ( x => basicDetails ( x ) ) ;
161
160
}
162
161
@@ -167,11 +166,11 @@ async function getById(id) {
167
166
168
167
async function create ( params ) {
169
168
// validate
170
- if ( await Account . findOne ( { email : params . email } ) ) {
169
+ if ( await db . Account . findOne ( { email : params . email } ) ) {
171
170
throw 'Email "' + params . email + '" is already registered' ;
172
171
}
173
172
174
- const account = new Account ( params ) ;
173
+ const account = new db . Account ( params ) ;
175
174
account . verified = Date . now ( ) ;
176
175
177
176
// hash password
@@ -189,7 +188,7 @@ async function update(id, params) {
189
188
const account = await getAccount ( id ) ;
190
189
191
190
// validate
192
- if ( account . email !== params . email && await Account . findOne ( { email : params . email } ) ) {
191
+ if ( account . email !== params . email && await db . Account . findOne ( { email : params . email } ) ) {
193
192
throw 'Email "' + params . email + '" is already taken' ;
194
193
}
195
194
@@ -215,17 +214,15 @@ async function _delete(id) {
215
214
216
215
async function getAccount ( id ) {
217
216
if ( ! db . isValidId ( id ) ) throw 'Account not found' ;
218
- const account = await Account . findById ( id ) ;
217
+ const account = await db . Account . findById ( id ) ;
219
218
if ( ! account ) throw 'Account not found' ;
220
219
return account ;
221
220
}
222
221
223
222
async function getRefreshToken ( token ) {
224
- const account = await Account . findOne ( ) . elemMatch ( 'refreshTokens' , { token } ) ;
225
- if ( ! account ) throw 'Invalid token' ;
226
- const refreshToken = account . refreshTokens . find ( x => x . token === token ) ;
227
- if ( ! refreshToken . isActive ) throw 'Invalid token' ;
228
- return { account, refreshToken } ;
223
+ const refreshToken = await db . RefreshToken . findOne ( { token } ) . populate ( 'account' ) ;
224
+ if ( ! refreshToken || ! refreshToken . isActive ) throw 'Invalid token' ;
225
+ return refreshToken ;
229
226
}
230
227
231
228
function hash ( password ) {
@@ -237,13 +234,14 @@ function generateJwtToken(account) {
237
234
return jwt . sign ( { sub : account . id , id : account . id } , config . secret , { expiresIn : '15m' } ) ;
238
235
}
239
236
240
- function generateRefreshToken ( ipAddress ) {
237
+ function generateRefreshToken ( account , ipAddress ) {
241
238
// create a refresh token that expires in 7 days
242
- return {
239
+ return new db . RefreshToken ( {
240
+ account : account . id ,
243
241
token : randomTokenString ( ) ,
244
242
expires : new Date ( Date . now ( ) + 7 * 24 * 60 * 60 * 1000 ) ,
245
243
createdByIp : ipAddress
246
- } ;
244
+ } ) ;
247
245
}
248
246
249
247
function randomTokenString ( ) {
@@ -255,7 +253,7 @@ function basicDetails(account) {
255
253
return { id, title, firstName, lastName, email, role, created, updated, isVerified } ;
256
254
}
257
255
258
- function sendVerificationEmail ( account , origin ) {
256
+ async function sendVerificationEmail ( account , origin ) {
259
257
let message ;
260
258
if ( origin ) {
261
259
const verifyUrl = `${ origin } /account/verify-email?token=${ account . verificationToken } ` ;
@@ -266,7 +264,7 @@ function sendVerificationEmail(account, origin) {
266
264
<p><code>${ account . verificationToken } </code></p>` ;
267
265
}
268
266
269
- sendEmail ( {
267
+ await sendEmail ( {
270
268
to : account . email ,
271
269
subject : 'Sign-up Verification API - Verify Email' ,
272
270
html : `<h4>Verify Email</h4>
@@ -275,15 +273,15 @@ function sendVerificationEmail(account, origin) {
275
273
} ) ;
276
274
}
277
275
278
- function sendAlreadyRegisteredEmail ( email , origin ) {
276
+ async function sendAlreadyRegisteredEmail ( email , origin ) {
279
277
let message ;
280
278
if ( origin ) {
281
279
message = `<p>If you don't know your password please visit the <a href="${ origin } /account/forgot-password">forgot password</a> page.</p>` ;
282
280
} else {
283
281
message = `<p>If you don't know your password you can reset it via the <code>/account/forgot-password</code> api route.</p>` ;
284
282
}
285
283
286
- sendEmail ( {
284
+ await sendEmail ( {
287
285
to : email ,
288
286
subject : 'Sign-up Verification API - Email Already Registered' ,
289
287
html : `<h4>Email Already Registered</h4>
@@ -292,7 +290,7 @@ function sendAlreadyRegisteredEmail(email, origin) {
292
290
} ) ;
293
291
}
294
292
295
- function sendPasswordResetEmail ( account , origin ) {
293
+ async function sendPasswordResetEmail ( account , origin ) {
296
294
let message ;
297
295
if ( origin ) {
298
296
const resetUrl = `${ origin } /account/reset-password?token=${ account . resetToken . token } ` ;
@@ -303,7 +301,7 @@ function sendPasswordResetEmail(account, origin) {
303
301
<p><code>${ account . resetToken . token } </code></p>` ;
304
302
}
305
303
306
- sendEmail ( {
304
+ await sendEmail ( {
307
305
to : account . email ,
308
306
subject : 'Sign-up Verification API - Reset Password' ,
309
307
html : `<h4>Reset Password Email</h4>
0 commit comments