summaryrefslogtreecommitdiffstats
path: root/backend/src
diff options
context:
space:
mode:
authorJan Tuomi <jan.tuomi@eficode.com>2020-02-05 11:35:03 +0200
committerJan Tuomi <jan.tuomi@eficode.com>2020-02-05 11:35:03 +0200
commit2011d3f6f9938825a36670779fa0d7894bda338f (patch)
tree8b1c073a4329432f1287ba50b90401b380b9301d /backend/src
parenta5ae2653d8ccdaf00270c0c8c8a30b88d7955114 (diff)
Implement session tokens securely
Diffstat (limited to 'backend/src')
-rw-r--r--backend/src/routes.js68
1 files changed, 43 insertions, 25 deletions
diff --git a/backend/src/routes.js b/backend/src/routes.js
index a762d3a..b0f3660 100644
--- a/backend/src/routes.js
+++ b/backend/src/routes.js
@@ -1,20 +1,21 @@
const express = require('express');
const sha512 = require('js-sha512');
const shortid = require('shortid');
-
-const salt = 'suola';
+const base64 = require('base-64');
const db = require('./db');
-const buildSessionCookie = ({ sessionId, isLogout, remember }) => {
+const buildSessionCookie = ({ username, sessionToken, isLogout, remember }) => {
if (!isLogout) {
+ const joinedToken = `${username}&${sessionToken}`;
+ const encodedToken = base64.encode(joinedToken);
if (remember) {
- return `sessionId=${sessionId}; Domain=localhost; HttpOnly; SameSite=strict;`;
+ return `sessionCookie=${encodedToken}; Domain=localhost; HttpOnly; SameSite=strict;`;
} else {
- return `sessionId=${sessionId}; Domain=localhost; HttpOnly; SameSite=strict; Max-Age=86400;`;
+ return `sessionCookie=${encodedToken}; Domain=localhost; HttpOnly; SameSite=strict; Max-Age=86400;`;
}
} else {
- return `sessionId=deleted; Domain=localhost; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=strict;`;
+ return `sessionCookie=deleted; Domain=localhost; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=strict;`;
}
};
@@ -23,29 +24,42 @@ const authedRouter = express.Router();
authedRouter.use(async (req, res, next) => {
const cookies = req.cookies;
- const { sessionId } = cookies;
+ const encodedCookie = cookies.sessionCookie;
- if (!sessionId) {
+ if (!encodedCookie) {
res.status(403);
return res.json({
message: 'Log in first!',
});
}
+ const sessionCookie = base64.decode(encodedCookie);
- const token = await db('tokens').where('value', sessionId).first();
+ const [ username, sessionToken ] = sessionCookie.split('&');
- if (!token) {
+ const user = await db('users').where({ username }).first();
+ if (!user) {
res.status(400);
res.set('Set-Cookie', buildSessionCookie({ isLogout: true }));
return res.json({
message: 'Invalid session token.',
- sessionId,
+ sessionCookie,
});
}
- const user = await db('users').where({ id: token.user_id }).first();
+ const salt = user.salt;
- req.sessionId = sessionId;
+ const sessionTokenHash = sha512(salt + sessionToken);
+ const token = await db('tokens').where({ type: 'session', value: sessionTokenHash });
+ if (!token) {
+ res.status(400);
+ res.set('Set-Cookie', buildSessionCookie({ isLogout: true }));
+ return res.json({
+ message: 'Invalid session token.',
+ sessionCookie,
+ });
+ }
+
+ req.sessionTokenHash = sessionTokenHash;
req.user = user;
next();
@@ -66,20 +80,24 @@ normalRouter.post('/register', async (req, res) => {
});
}
- const existingUuser = await db('users').where({ username }).first();
- if (existingUuser) {
+ const existingUser = await db('users').where({ username }).first();
+ if (existingUser) {
res.status(400);
return res.json({
error: 'Username already taken.',
});
}
+ const salt = shortid.generate();
const pwHash = sha512(salt + password);
- const sessionId = shortid.generate();
- const user = (await db('users').insert({ username, password: pwHash }).returning('*'))[0];
- await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionId });
- res.set('Set-Cookie', buildSessionCookie({ sessionId, remember: false, isLogout: false }));
+ const sessionToken = shortid.generate();
+ const sessionTokenHash = sha512(salt + sessionToken);
+
+ const user = (await db('users').insert({ username, password: pwHash, salt }).returning('*'))[0];
+ await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionTokenHash });
+
+ res.set('Set-Cookie', buildSessionCookie({ username, sessionToken, remember: false, isLogout: false }));
return res.json({
username,
});
@@ -97,10 +115,12 @@ normalRouter.post('/login', async (req, res) => {
const user = await db('users').where({ username }).first();
if (user) {
- const sessionId = shortid.generate();
- await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionId });
+ const sessionToken = shortid.generate();
+ const sessionTokenHash = sha512(user.salt + sessionToken);
+
+ await db('tokens').insert({ user_id: user.id, type: 'session', value: sessionTokenHash });
- res.set('Set-Cookie', buildSessionCookie({ sessionId, remember, isLogout: false }));
+ res.set('Set-Cookie', buildSessionCookie({ username, sessionToken, remember, isLogout: false }));
return res.json({
username,
@@ -114,9 +134,7 @@ normalRouter.post('/login', async (req, res) => {
});
authedRouter.post('/logout', async (req, res) => {
- if (req.user && req.sessionId) {
- await db('tokens').where({ value: req.sessionId }).delete();
- }
+ await db('tokens').where({ type: 'session', user_id: req.user.id, value: req.sessionTokenHash }).delete();
res.status(204);
res.set('Set-Cookie', buildSessionCookie({ isLogout: true }));
res.send();