Issues (50)

v1/models/auth.js (13 issues)

1
const bcrypt = require('bcryptjs');
2
const jwt = require('jsonwebtoken');
3
const sanitize = require('mongo-sanitize'); // To prevent malicious users overwriting (NoSQL Injection)
4
const { MongoClient } = require("mongodb");
5
const mongoURI = process.env.DBURI;
6
const api_token = process.env.API_TOKEN
7
const jwtSecret = process.env.JWT_SECRET;
8
9
const auth = {
10
    checkAPIKey: async function(req, res, next) {  // Need to go through a check first for those request that don't need an API Key.
11
        if ( req.path == '/') { // Documentation
12
            return next();
13
        }
14
15
        if ( req.path == '/v1') { // Documentation
16
            return next();
17
        }
18
19
        if ( req.path == '/auth/login/google') { // Google
20
            return next();
21
        }
22
23
        if ( req.path == '/auth/google/callback') { // Google
24
            return next();
25
        }
26
27
        if ( req.path == '/auth/logout/google') { // Google
28
            return next();
29
        }
30
31
        if ( req.path == '/auth/login/google/error') { // Google
32
            return next();
33
        }
34
35
        auth.validAPIKey(req.query.api_key || req.body.api_key, next, req.path, res);
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
36
    },
37
38
    checkValidAdmin: async function(req, res, next) {
39
        if (req.body.api_key === process.env.REACT_APP_ADMIN_API_KEY || req.query.api_key === process.env.REACT_APP_ADMIN_API_KEY) {
40
            return auth.adminCheckToken(req, res, next);
41
        }
42
43
        return res.status(401).json({
44
            errors: {
45
                status: 401,
46
                source: req.path,
47
                title: "Not a admin",
48
                detail: "You don't have access to this area."
49
            }
50
        });
51
    },
52
53
    validTokenKey: async function(req, res, next) {  // Look if token is valid
54
        if (req.headers["x-access-token"] !== undefined) {
55
            if (req.body.api_key == process.env.REACT_APP_ADMIN_API_KEY || req.query.api_key == process.env.REACT_APP_ADMIN_API_KEY) {
56
                return auth.adminCheckToken(req, res, next);
57
            } else if (req.body.api_key == process.env.REACT_APP_USER_MOBILE_API_KEY || req.query.api_key == process.env.REACT_APP_USER_MOBILE_API_KEY) {
58
                return auth.userCheckToken(req, res, next);
59
            } else if (req.body.api_key == process.env.REACT_APP_USER_WEBB_API_KEY || req.query.api_key == process.env.REACT_APP_USER_WEBB_API_KEY) {
60
                return auth.userCheckToken(req, res, next);
61
            }
62
        } else if (req.user) {
63
            return auth.userAuthenticated(req, res, next);
64
        }
65
66
        return res.status(401).json({
67
            errors: {
68
                status: 401,
69
                source: req.path,
70
                title: "Valid Token key",
71
                detail: "No valid Token key provided."
72
            }
73
        });
74
    },
75
76
    validAPIKey: async function(apiKey, next, path, res) {  // Check if it's correct API KEY
77
        if (!api_token.includes(apiKey) ) {
78
            return res.status(401).json({
79
                errors: {
80
                    status: 401,
81
                    source: path,
82
                    title: "Valid API key",
83
                    detail: "No valid API key provided."
84
                }
85
            });
86
        }
87
        
88
        return next();
89
    },
90
91
    adminLogin: async function(res, body, path) { // Admin login
92
        const adminEmail = sanitize(body.email);
93
        const adminPassword = sanitize(body.password);
94
        const apiKey = sanitize(body.apiKey);
95
96
        // If adminEmail or adminPassword is undefined
97
        if (!adminEmail || !adminPassword) {
98
            return res.status(401).json({
99
                errors: {
100
                    status: 401,
101
                    source: "/auth/admin/login",
102
                    title: "Email or password missing",
103
                    detail: "Email or password missing in request"
104
                }
105
            });
106
        }
107
108
        // Find the email in database
109
        let client = new MongoClient(mongoURI);
110
        try {
111
            let db = client.db("spark-rentals");
112
            let admins_collection = db.collection("admins");
113
            let admin = await admins_collection.findOne({email: adminEmail});
114
115
            // If email not found in database
116
            if (admin === null) {
117
                return res.status(401).json({
118
                    errors: {
119
                        status: 401,
120
                        source: "POST /auth" + path,
121
                        title: "Admin not found",
122
                        detail: "Admin with provided email not found."
123
                    }
124
                });
125
            }
126
127
            // Compare bcrypt password in database with password sent in.
128
            bcrypt.compare(adminPassword, admin.password, (err, result) => {
129
                if (err) {
130
                    return res.status(500).json({
131
                        errors: {
132
                            status: 500,
133
                            source: "POST /auth" + path,
134
                            title: "bcrypt error",
135
                            detail: "bcrypt error"
136
                        }
137
                    });
138
                }
139
140
                // If everything goes allright it created jwt token and send a response sucess with jwt token
141
                if (!result) {
142
                    // If password is not the same as in database
143
                    return res.status(401).json({
144
                        errors: {
145
                            status: 401,
146
                            source: "POST /auth" + path,
147
                                title: "Wrong password",
148
                            detail: "Password is incorrect."
149
                        }
150
                    });
151
                }
152
                let payload = { api_key: apiKey, email: admin.email };
153
                let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: 60 * 60 });
154
155
                return res.json({
156
                    data: {
157
                        type: "success",
158
                        message: "Admin logged in",
159
                        user: payload,
160
                        token: jwtToken
161
                    }
162
                });
163
            });
164
        } catch(e) { return res.status(500).send(); } finally { await client.close(); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
165
    },
166
167
    adminRegister: async function(res, body, path) { // Register a new Admin
168
        const adminEmail = sanitize(body.email);
169
        const adminPassword = sanitize(body.password);
170
        const adminFirstName = sanitize(body.firstName);
171
        const adminLastName = sanitize(body.lastName);
172
173
        // If adminEmail or adminPassword is undefined
174
        if (!adminEmail || !adminPassword) {
175
            return res.status(401).json({
176
                errors: {
177
                    status: 401,
178
                    source: "POST /auth" + path,
179
                    title: "Email or password missing",
180
                    detail: "Email or password missing in request"
181
                }
182
            });
183
        } else if (!adminFirstName || !adminLastName) { // If adminFirstName or adminLastName is undefined
184
            return res.status(401).json({
185
                errors: {
186
                    status: 401,
187
                    source: "POST /auth" + path,
188
                    title: "First name or last name missing",
189
                    detail: "First name or last name missing in request"
190
                }
191
            });
192
        }
193
194
        // Find the email in database
195
        let client = new MongoClient(mongoURI);
196
        try {
197
            let db = client.db("spark-rentals");
198
            let admins_collection = db.collection("admins");
199
            let admin = await admins_collection.findOne({email: adminEmail});
200
201
            // If email found in database
202
            if (admin !== null) {
203
                client.close();
204
                return res.status(401).json({
205
                    errors: {
206
                        status: 403,
207
                        source: "POST /auth" + path,
208
                        title: "Admin alredy created",
209
                        detail: "Admin with provided email is alredy created in database."
210
                    }
211
                });
212
            }
213
214
            // bcrypt the password
215
            bcrypt.hash(adminPassword, 10, async function(err, hash) {
216
                if (err) {
217
                    client.close();
218
                    return res.status(500).json({ // if error with bcrypt
219
                        errors: {
220
                            status: 500,
221
                            source: "POST /auth" + path,
222
                            title: "bcrypt error",
223
                            detail: "bcrypt error"
224
                        }
225
                    });
226
                }
227
    
228
                // create a admin object
229
                adminCreate = {
0 ignored issues
show
The variable adminCreate seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.adminCreate.
Loading history...
230
                    firstName: adminFirstName,
231
                    lastName: adminLastName,
232
                    email: adminEmail,
233
                    password: hash,
234
                }
235
236
                // Insert the admin object to the database
237
                await admins_collection.insertOne(adminCreate);
238
                client.close();
239
240
                // Return sucess
241
                return res.status(201).json({
242
                    data: {
243
                        message: "User successfully registered."
244
                    }
245
                });
246
            });
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
247
        } catch(e) { return res.status(500).send(); }
248
    },
249
250
    adminCheckToken: function(req, res, next) { // Check the x-access-token from admin
251
        let token = req.headers['x-access-token'];
252
253
        if (!token) {
254
            return res.status(401).json({ // If no token in request headers response with error code 401
255
                errors: {
256
                    status: 401,
257
                    source: req.path,
258
                    title: "No admin token",
259
                    detail: "No admin token provided in request headers"
260
                }
261
            });
262
        }
263
264
        jwt.verify(token, jwtSecret, function(err, decoded) { // Verify the token
265
            if (err) {
266
                return res.status(500).json({ // If error response with error code 500
267
                    errors: {
268
                        status: 500,
269
                        source: req.path,
270
                        title: "Failed authentication",
271
                        detail: err.message
272
                    }
273
                });
274
            }
275
276
            req.admin = {};
277
            req.admin.api_key = decoded.api_key;
278
            req.admin.email = decoded.email;
279
280
            return next(); // sucess
281
        });
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
282
    
283
    },
284
285
    userAuthenticated: function(req,res,next) {
286
        if (req.user || req.isAuthenticated()) {
287
            return next();
288
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
289
            res.status(401).send("You mustlogin first!")
290
        }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
291
    },
292
293
    userLogin: async function(res, body, path) { // Admin login
294
        const userEmail = sanitize(body.email);
295
        const userPassword = sanitize(body.password);
296
        const apiKey = sanitize(body.api_key);
0 ignored issues
show
The constant apiKey seems to be never used. Consider removing it.
Loading history...
297
298
        // If userEmail or userPassword is undefined
299
        if (!userEmail || !userPassword) {
300
            return res.status(401).json({
301
                errors: {
302
                    status: 401,
303
                    source: "POST /auth" + path,
304
                    title: "Email or password missing",
305
                    detail: "Email or password missing in request"
306
                }
307
            });
308
        }
309
310
        // Find the email in database
311
        let client = new MongoClient(mongoURI);
312
        try {
313
            let db = client.db("spark-rentals");
314
            let users_collection = db.collection("users");
315
            let user = await users_collection.findOne({email: userEmail});
316
317
            // If email not found in database
318
            if (user === null) {
319
                return res.status(401).json({
320
                    errors: {
321
                        status: 401,
322
                        source: "POST auth" + path,
323
                        title: "User not found",
324
                        detail: "User with provided email not found."
325
                    }
326
                });
327
            }
328
329
            // Compare bcrypt password in database with password sent in.
330
            bcrypt.compare(userPassword, user.password, (err, result) => {
331
                if (err) {
332
                    return res.status(500).json({
333
                        errors: {
334
                            status: 500,
335
                            source: "POST auth" + path,
336
                            title: "bcrypt error",
337
                            detail: "bcrypt error"
338
                        }
339
                    });
340
                }
341
342
                // If everything goes allright it creates a jwt token and send a response sucess with jwt token
343
                if (result) {
344
                    let payload = {id: user._id, email: user.email };
345
                    let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: 60 * 60 });
346
347
                    return res.json({
348
                        data: {
349
                            type: "success",
350
                            message: "User logged in",
351
                            user: payload,
352
                            token: jwtToken
353
                        }
354
                    });
355
                }
356
357
                // If password is not the same as in database
358
                return res.status(401).json({
359
                    errors: {
360
                        status: 401,
361
                        source: "POST auth" + path,
362
                        title: "Wrong password",
363
                        detail: "Password is incorrect."
364
                    }
365
                });
366
            });
367
        } catch(e) { return res.status(500).send(); } finally { await client.close(); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
368
    },
369
370
    userRegister: async function(res, body, path) { // Register a new Admin
371
        const userEmail = sanitize(body.email);
372
        const userPassword = sanitize(body.password);
373
        const userFirstName = sanitize(body.firstName);
374
        const userLastName = sanitize(body.lastName);
375
        const userPhoneNumber = sanitize(body.phoneNumber);
376
377
        // If adminEmail or adminPassword is undefined
378
        if (!userEmail || !userPassword) {
379
            return res.status(401).json({
380
                errors: {
381
                    status: 401,
382
                    source: "POST auth" + path,
383
                    title: "Email or password missing",
384
                    detail: "Email or password missing in request"
385
                }
386
            });
387
        } else if (!userFirstName || !userLastName) { // If userFirstName or userLastName is undefined
388
            return res.status(401).json({
389
                errors: {
390
                    status: 401,
391
                    source: "POST auth" + path,
392
                    title: "First name or last name missing",
393
                    detail: "First name or last name missing in request"
394
                }
395
            });
396
        } else if (!userPhoneNumber) {
397
            return res.status(401).json({
398
                errors: {
399
                    status: 401,
400
                    source: "POST auth" + path,
401
                    title: "Phonenumber missing",
402
                    detail: "Phonenumber missing missing in request"
403
                }
404
            });
405
        }
406
407
        // Find the email in database
408
        let client = new MongoClient(mongoURI);
409
        try {
410
            let db = client.db("spark-rentals");
411
            let users_collection = db.collection("users");
412
            let user = await users_collection.findOne({email: userEmail});
413
414
            // If email found in database
415
            if (user !== null) {
416
                return res.status(401).json({
417
                    errors: {
418
                        status: 403,
419
                        source: "POST auth" + path,
420
                        title: "User alredy created",
421
                        detail: "User with provided email is alredy created in database."
422
                    }
423
                });
424
            }
425
426
            // bcrypt the password
427
            bcrypt.hash(userPassword, 10, async function(err, hash) {
428
                if (err) {
429
                    return res.status(500).json({ // if error with bcrypt
430
                        errors: {
431
                            status: 500,
432
                            source: "POST auth" + path,
433
                            title: "bcrypt error",
434
                            detail: "bcrypt error"
435
                        }
436
                    });
437
                }
438
    
439
                // create a admin object
440
                userCreate = {
0 ignored issues
show
The variable userCreate seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.userCreate.
Loading history...
441
                    googleId: null,
442
                    firstName: userFirstName,
443
                    lastName: userLastName,
444
                    phoneNumber: userPhoneNumber,
445
                    email: userEmail,
446
                    password: hash,
447
                    balance: 0,
448
                    history: []
449
                }
450
451
                let findUser = null;
452
                let registerClient = new MongoClient(mongoURI);
453
                try {
454
                    let db = registerClient.db("spark-rentals");
455
                    let users_collection = db.collection("users");
456
                    await users_collection.insertOne(userCreate);
457
                    findUser = await users_collection.findOne({email: userEmail});
458
                } catch(e) { return res.status(500); } finally { await client.close(); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
459
460
                return res.status(201).json({
461
                    data: {
462
                        userId: findUser._id,
463
                        message: "User successfully registered."
464
                    }
465
                });
466
            });
467
        } catch(e) { return res.status(500).send(); } finally { await client.close(); }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
468
    },
469
470
    userCheckToken: function(req, res, next) { // Check the x-access-token from user
471
        let token = req.headers['x-access-token'];
472
473
        if (!token) {
474
            return res.status(401).json({ // If no token in request headers response with error code 401
475
                errors: {
476
                    status: 401,
477
                    source: req.path,
478
                    title: "No user token",
479
                    detail: "No user token provided in request headers"
480
                }
481
            });
482
        }
483
484
        jwt.verify(token, jwtSecret, function(err, decoded) { // Verify the token
485
            if (err) {
486
                return res.status(500).json({ // If error response with error code 500
487
                    errors: {
488
                        status: 500,
489
                        source: req.path,
490
                        title: "Failed authentication",
491
                        detail: err.message
492
                    }
493
                });
494
            }
495
496
            req.user = {};
497
            req.user.api_key = decoded.api_key;
498
            req.user.email = decoded.email;
499
500
            return next();
501
        });
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
502
    
503
    },
504
}
505
506
module.exports = auth;