-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.js
198 lines (159 loc) · 5.21 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const {
refreshTokens, COOKIE_OPTIONS, generateToken, generateRefreshToken,
getCleanUser, verifyToken, clearTokens, handleResponse,
} = require('./utils');
const app = express();
const port = process.env.PORT || 4000;
// list of the users to be consider as a database for example
const userList = [
{
userId: "123",
password: "clue",
name: "Clue",
username: "clue",
isAdmin: true
},
{
userId: "456",
password: "mediator",
name: "Mediator",
username: "mediator",
isAdmin: true
},
{
userId: "789",
password: "123456",
name: "Clue Mediator",
username: "cluemediator",
isAdmin: true
}
]
// enable CORS
app.use(cors({
origin: 'http://localhost:3000', // url of the frontend application
credentials: true // set credentials true for secure httpOnly cookie
}));
// parse application/json
app.use(bodyParser.json());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// use cookie parser for secure httpOnly cookie
app.use(cookieParser(process.env.COOKIE_SECRET));
// middleware that checks if JWT token exists and verifies it if it does exist.
// In all private routes, this helps to know if the request is authenticated or not.
const authMiddleware = function (req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['authorization'];
if (!token) return handleResponse(req, res, 401);
token = token.replace('Bearer ', '');
// get xsrf token from the header
const xsrfToken = req.headers['x-xsrf-token'];
if (!xsrfToken) {
return handleResponse(req, res, 403);
}
// verify xsrf token
const { signedCookies = {} } = req;
const { refreshToken } = signedCookies;
if (!refreshToken || !(refreshToken in refreshTokens) || refreshTokens[refreshToken] !== xsrfToken) {
return handleResponse(req, res, 401);
}
// verify token with secret key and xsrf token
verifyToken(token, xsrfToken, (err, payload) => {
if (err)
return handleResponse(req, res, 401);
else {
req.user = payload; //set the user to req so other routes can use it
next();
}
});
}
// validate user credentials
app.post('/users/signin', function (req, res) {
const user = req.body.username;
const pwd = req.body.password;
// return 400 status if username/password is not exist
if (!user || !pwd) {
return handleResponse(req, res, 400, null, "Username and Password required.");
}
const userData = userList.find(x => x.username === user && x.password === pwd);
// return 401 status if the credential is not matched
if (!userData) {
return handleResponse(req, res, 401, null, "Username or Password is Wrong.");
}
// get basic user details
const userObj = getCleanUser(userData);
// generate access token
const tokenObj = generateToken(userData);
// generate refresh token
const refreshToken = generateRefreshToken(userObj.userId);
// refresh token list to manage the xsrf token
refreshTokens[refreshToken] = tokenObj.xsrfToken;
// set cookies
res.cookie('refreshToken', refreshToken, COOKIE_OPTIONS);
res.cookie('XSRF-TOKEN', tokenObj.xsrfToken);
return handleResponse(req, res, 200, {
user: userObj,
token: tokenObj.token,
expiredAt: tokenObj.expiredAt
});
});
// handle user logout
app.post('/users/logout', (req, res) => {
clearTokens(req, res);
return handleResponse(req, res, 204);
});
// verify the token and return new tokens if it's valid
app.post('/verifyToken', function (req, res) {
const { signedCookies = {} } = req;
const { refreshToken } = signedCookies;
if (!refreshToken) {
return handleResponse(req, res, 204);
}
// verify xsrf token
const xsrfToken = req.headers['x-xsrf-token'];
if (!xsrfToken || !(refreshToken in refreshTokens) || refreshTokens[refreshToken] !== xsrfToken) {
return handleResponse(req, res, 401);
}
// verify refresh token
verifyToken(refreshToken, '', (err, payload) => {
if (err) {
return handleResponse(req, res, 401);
}
else {
const userData = userList.find(x => x.userId === payload.userId);
if (!userData) {
return handleResponse(req, res, 401);
}
// get basic user details
const userObj = getCleanUser(userData);
// generate access token
const tokenObj = generateToken(userData);
// refresh token list to manage the xsrf token
refreshTokens[refreshToken] = tokenObj.xsrfToken;
res.cookie('XSRF-TOKEN', tokenObj.xsrfToken);
// return the token along with user details
return handleResponse(req, res, 200, {
user: userObj,
token: tokenObj.token,
expiredAt: tokenObj.expiredAt
});
}
});
});
// get list of the users
app.get('/users/getList', authMiddleware, (req, res) => {
const list = userList.map(x => {
const user = { ...x };
delete user.password;
return user;
});
return handleResponse(req, res, 200, { random: Math.random(), userList: list });
});
app.listen(port, () => {
console.log('Server started on: ' + port);
});