v1/models/auth.js   A
last analyzed

Complexity

Total Complexity 35
Complexity/F 2.19

Size

Lines of Code 297
Function Count 16

Duplication

Duplicated Lines 149
Ratio 50.17 %

Test Coverage

Coverage 88.16%

Importance

Changes 0
Metric Value
wmc 35
eloc 191
mnd 19
bc 19
fnc 16
dl 149
loc 297
ccs 67
cts 76
cp 0.8816
rs 9.6
bpm 1.1875
cpm 2.1875
noi 13
c 0
b 0
f 0

6 Functions

Rating   Name   Duplication   Size   Complexity  
A auth.js ➔ checkToken 29 33 4
B auth.js ➔ getNewAPIKey 0 36 5
B auth.js ➔ register 34 51 6
B auth.js ➔ isValidAPIKey 25 27 6
B auth.js ➔ getUniqueAPIKey 0 37 6
C auth.js ➔ login 61 81 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1 1
const db = require("../db/database.js");
2 1
const hat = require("hat");
3 1
const validator = require("email-validator");
4
5 1
const bcrypt = require('bcryptjs');
6 1
const jwt = require('jsonwebtoken');
7
8
let config;
9
10 1
try {
11 1
    config = require('../../config/config.json');
12
} catch (error) {
13 1
    console.error(error);
14
}
15
16 2
const jwtSecret = process.env.JWT_SECRET || config.secret;
17
18 1
module.exports = (function () {
19
    function isValidAPIKey(apiKey, next, path, res) {
20 100 View Code Duplication
        db.get("SELECT email FROM apikeys WHERE key = ?", apiKey, (err, row) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
21 100
            if (err) {
22
                return res.status(500).json({
23
                    errors: {
24
                        status: 500,
25
                        source: path,
26
                        title: "Database error",
27
                        detail: err.message
28
                    }
29
                });
30
            }
31
32 100
            if (row !== undefined) {
33 91
                return next();
34
            }
35
36 9
            res.status(401).json({
37
                errors: {
38
                    status: 401,
39
                    source: path,
40
                    title: "Valid API key",
41
                    detail: "No valid API key provided."
42
                }
43
            });
0 ignored issues
show
Best Practice introduced by
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...
44
        });
45
    }
46
47
    function getNewAPIKey(res, path, email) {
48 11
        if (email === undefined || !validator.validate(email)) {
49 2
            res.status(401).json({
50
                errors: {
51
                    status: 401,
52
                    source: path,
53
                    title: "Valid email",
54
                    detail: "A valid email address is required to obtain an API key."
55
                }
56
            });
57
        } else {
58 9
            db.get("SELECT email, key FROM apikeys WHERE email = ?", email, (err, row) => {
59 9
                if (err) {
60
                    return res.status(500).json({
61
                        errors: {
62
                            status: 500,
63
                            source: path,
64
                            title: "Database error",
65
                            detail: err.message
66
                        }
67
                    });
68
                }
69
70 9
                if (row !== undefined) {
71 1
                    return res.json({
72
                        data: {
73
                            message: "Email address already used for api key.",
74
                            apiKey: row.key
75
                        }
76
                    });
77
                }
78
79 8
                getUniqueAPIKey(res, path, email);
0 ignored issues
show
Best Practice introduced by
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...
80
            });
81
        }
82
    }
83
84
    function getUniqueAPIKey(res, path, email) {
85 8
        const apiKey = hat();
86
87 8
        db.get("SELECT key FROM apikeys WHERE key = ?", apiKey, (err, row) => {
88 8
            if (err) {
89
                return res.status(401).json({
90
                    errors: {
91
                        status: 401,
92
                        source: path,
93
                        title: "Database error",
94
                        detail: err.message
95
                    }
96
                });
97
            }
98
99 8
            if (row === undefined) {
100 8
                db.run("INSERT INTO apikeys (key, email) VALUES (?, ?)",
101
                    apiKey,
102
                    email, (err) => {
103 8
                        if (err) {
104
                            return res.status(401).json({
105
                                errors: {
106
                                    status: 401,
107
                                    source: path,
108
                                    title: "Database error",
109
                                    detail: err.message
110
                                }
111
                            });
112
                        }
113
114 8
                        res.json({ data: { key: apiKey }});
0 ignored issues
show
Best Practice introduced by
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...
115
                    });
0 ignored issues
show
Best Practice introduced by
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...
116
            } else {
117
                getUniqueAPIKey(res, email);
0 ignored issues
show
Best Practice introduced by
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...
118
            }
119
        });
120
    }
121
122
    function login(res, body) {
123 6
        const email = body.email;
124 6
        const password = body.password;
125 6
        const apiKey = body.api_key;
126
127 6
        if (!email || !password) {
128 2
            return res.status(401).json({
129
                errors: {
130
                    status: 401,
131
                    source: "/login",
132
                    title: "Email or password missing",
133
                    detail: "Email or password missing in request"
134
                }
135
            });
136
        }
137
138 4
        db.get("SELECT * FROM users WHERE apiKey = ? AND email = ?",
139
            apiKey,
140
            email,
141 View Code Duplication
            (err, rows) => {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
142 4
                if (err) {
143
                    return res.status(500).json({
144
                        errors: {
145
                            status: 500,
146
                            source: "/login",
147
                            title: "Database error",
148
                            detail: err.message
149
                        }
150
                    });
151
                }
152
153 4
                if (rows === undefined) {
154 1
                    return res.status(401).json({
155
                        errors: {
156
                            status: 401,
157
                            source: "/login",
158
                            title: "User not found",
159
                            detail: "User with provided email not found."
160
                        }
161
                    });
162
                }
163
164 3
                const user = rows;
165
166 3
                bcrypt.compare(password, user.password, (err, result) => {
167 3
                    if (err) {
168
                        return res.status(500).json({
169
                            errors: {
170
                                status: 500,
171
                                source: "/login",
172
                                title: "bcrypt error",
173
                                detail: "bcrypt error"
174
                            }
175
                        });
176
                    }
177
178 3
                    if (result) {
179 2
                        let payload = { api_key: user.apiKey, email: user.email };
180 2
                        let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: '24h' });
181
182 2
                        return res.json({
183
                            data: {
184
                                type: "success",
185
                                message: "User logged in",
186
                                user: payload,
187
                                token: jwtToken
188
                            }
189
                        });
190
                    } 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...
191 1
                        return res.status(401).json({
192
                            errors: {
193
                                status: 401,
194
                                source: "/login",
195
                                title: "Wrong password",
196
                                detail: "Password is incorrect."
197
                            }
198
                        });
199
                    }
200
                });
0 ignored issues
show
Best Practice introduced by
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...
201
            });
0 ignored issues
show
Best Practice introduced by
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...
202
    }
203
204
    function register(res, body) {
205 5
        const email = body.email;
206 5
        const password = body.password;
207 5
        const apiKey = body.api_key;
208
209 5
        if (!email || !password) {
210 2
            return res.status(401).json({
211
                errors: {
212
                    status: 401,
213
                    source: "/register",
214
                    title: "Email or password missing",
215
                    detail: "Email or password missing in request"
216
                }
217
            });
218
        }
219
220 3 View Code Duplication
        bcrypt.hash(password, 10, function(err, hash) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
221 3
            if (err) {
222
                return res.status(500).json({
223
                    errors: {
224
                        status: 500,
225
                        source: "/register",
226
                        title: "bcrypt error",
227
                        detail: "bcrypt error"
228
                    }
229
                });
230
            }
231
232 3
            db.run("INSERT INTO users (apiKey, email, password) VALUES (?, ?, ?)",
233
                apiKey,
234
                email,
235
                hash, (err) => {
236 3
                    if (err) {
237 1
                        return res.status(500).json({
238
                            errors: {
239
                                status: 500,
240
                                source: "/register",
241
                                title: "Database error",
242
                                detail: err.message
243
                            }
244
                        });
245
                    }
246
247 2
                    res.status(201).json({
248
                        data: {
249
                            message: "User successfully registered."
250
                        }
251
                    });
0 ignored issues
show
Best Practice introduced by
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...
252
                });
0 ignored issues
show
Best Practice introduced by
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...
253
        });
0 ignored issues
show
Best Practice introduced by
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...
254
    }
255
256
    function checkToken(req, res, next) {
257 13
        var token = req.headers['x-access-token'];
258
259 13 View Code Duplication
        if (token) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
260 10
            jwt.verify(token, jwtSecret, function(err, decoded) {
261 10
                if (err) {
262
                    return res.status(500).json({
263
                        errors: {
264
                            status: 500,
265
                            source: req.path,
266
                            title: "Failed authentication",
267
                            detail: err.message
268
                        }
269
                    });
270
                }
271
272 10
                req.user = {};
273 10
                req.user.api_key = decoded.api_key;
274 10
                req.user.email = decoded.email;
275
276 10
                next();
0 ignored issues
show
Best Practice introduced by
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...
277
            });
0 ignored issues
show
Best Practice introduced by
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...
278
        } else {
279 3
            return res.status(401).json({
280
                errors: {
281
                    status: 401,
282
                    source: req.path,
283
                    title: "No token",
284
                    detail: "No token provided in request headers"
285
                }
286
            });
287
        }
288
    }
289
290 1
    return {
291
        isValidAPIKey: isValidAPIKey,
292
        getNewAPIKey: getNewAPIKey,
293
        login: login,
294
        register: register,
295
        checkToken: checkToken
296
    };
297
}());
298