BETTERZON-100: Switching to cookies for session management (#46)

* BETTERZON-100: Switching session handling to cookies

* BETTERZON-100: Some code reformatting

* BETTERZON-100: Some more code reformatting
This commit is contained in:
Patrick 2021-05-13 18:47:50 +02:00 committed by GitHub
parent 5cc91654c3
commit cb55cae692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 5040 additions and 106 deletions

4984
Backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,9 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/cookie-parser": "^1.4.2",
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",

View File

@ -16,6 +16,8 @@ import {notFoundHandler} from './middleware/notFound.middleware';
import {usersRouter} from './models/users/users.router'; import {usersRouter} from './models/users/users.router';
import {pricealarmsRouter} from './models/pricealarms/pricealarms.router'; import {pricealarmsRouter} from './models/pricealarms/pricealarms.router';
const cookieParser = require('cookie-parser');
dotenv.config(); dotenv.config();
@ -39,6 +41,7 @@ const app = express();
app.use(helmet()); app.use(helmet());
app.use(cors()); app.use(cors());
app.use(express.json()); app.use(express.json());
app.use(cookieParser());
app.use('/products', productsRouter); app.use('/products', productsRouter);
app.use('/categories', categoriesRouter); app.use('/categories', categoriesRouter);
app.use('/manufacturers', manufacturersRouter); app.use('/manufacturers', manufacturersRouter);

View File

@ -1,5 +1,5 @@
import HttpException from "../common/http-exception"; import HttpException from '../common/http-exception';
import { Request, Response, NextFunction } from "express"; import {Request, Response, NextFunction} from 'express';
export const errorHandler = ( export const errorHandler = (
error: HttpException, error: HttpException,
@ -9,7 +9,7 @@ export const errorHandler = (
) => { ) => {
const status = error.statusCode || 500; const status = error.statusCode || 500;
const message = const message =
error.message || "It's not you. It's us. We are having some problems."; error.message || 'It\'s not you. It\'s us. We are having some problems.';
response.status(status).send(message); response.status(status).send(message);
}; };

View File

@ -1,4 +1,4 @@
import { Request, Response, NextFunction } from "express"; import {Request, Response, NextFunction} from 'express';
export const notFoundHandler = ( export const notFoundHandler = (
request: Request, request: Request,
@ -6,7 +6,7 @@ export const notFoundHandler = (
next: NextFunction next: NextFunction
) => { ) => {
const message = "Resource not found"; const message = 'Resource not found';
response.status(404).send(message); response.status(404).send(message);
}; };

View File

@ -27,7 +27,7 @@ categoriesRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(categories); res.status(200).send(categories);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -46,7 +46,7 @@ categoriesRouter.get('/:id', async (req: Request, res: Response) => {
res.status(200).send(category); res.status(200).send(category);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -65,6 +65,6 @@ categoriesRouter.get('/search/:term', async (req: Request, res: Response) => {
res.status(200).send(categories); res.status(200).send(categories);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -27,7 +27,7 @@ manufacturersRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(manufacturers); res.status(200).send(manufacturers);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -46,7 +46,7 @@ manufacturersRouter.get('/:id', async (req: Request, res: Response) => {
res.status(200).send(manufacturer); res.status(200).send(manufacturer);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -65,6 +65,6 @@ manufacturersRouter.get('/search/:term', async (req: Request, res: Response) =>
res.status(200).send(manufacturer); res.status(200).send(manufacturer);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -23,17 +23,8 @@ export const pricealarmsRouter = express.Router();
pricealarmsRouter.get('/', async (req: Request, res: Response) => { pricealarmsRouter.get('/', async (req: Request, res: Response) => {
try { try {
// Authenticate user // Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? ''; const user_ip = req.connection.remoteAddress ?? '';
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
if (!session_id || !session_key) {
// Missing
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
return;
}
const user = await UserService.checkSession(session_id, session_key, user_ip);
const priceAlarms = await PriceAlarmsService.getPriceAlarms(user.user_id); const priceAlarms = await PriceAlarmsService.getPriceAlarms(user.user_id);
@ -48,17 +39,8 @@ pricealarmsRouter.get('/', async (req: Request, res: Response) => {
pricealarmsRouter.post('/create', async (req: Request, res: Response) => { pricealarmsRouter.post('/create', async (req: Request, res: Response) => {
try { try {
// Authenticate user // Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? ''; const user_ip = req.connection.remoteAddress ?? '';
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
if (!session_id || !session_key) {
// Missing
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
return;
}
const user = await UserService.checkSession(session_id, session_key, user_ip);
// Get info for price alarm creation // Get info for price alarm creation
const product_id = req.body.product_id; const product_id = req.body.product_id;
@ -90,17 +72,8 @@ pricealarmsRouter.post('/create', async (req: Request, res: Response) => {
pricealarmsRouter.put('/update', async (req: Request, res: Response) => { pricealarmsRouter.put('/update', async (req: Request, res: Response) => {
try { try {
// Authenticate user // Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? ''; const user_ip = req.connection.remoteAddress ?? '';
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
if (!session_id || !session_key) {
// Missing
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
return;
}
const user = await UserService.checkSession(session_id, session_key, user_ip);
// Get info for price alarm creation // Get info for price alarm creation
const alarm_id = req.body.alarm_id; const alarm_id = req.body.alarm_id;

View File

@ -40,7 +40,7 @@ pricesRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(prices); res.status(200).send(prices);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -59,7 +59,7 @@ pricesRouter.get('/:id', async (req: Request, res: Response) => {
res.status(200).send(price); res.status(200).send(price);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -78,7 +78,7 @@ pricesRouter.get('/bestDeals/:amount', async (req: Request, res: Response) => {
res.status(200).send(prices); res.status(200).send(prices);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -97,6 +97,6 @@ pricesRouter.get('/byProduct/list/:ids', async (req: Request, res: Response) =>
res.status(200).send(prices); res.status(200).send(prices);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -27,7 +27,7 @@ productsRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(products); res.status(200).send(products);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -46,7 +46,7 @@ productsRouter.get('/:id', async (req: Request, res: Response) => {
res.status(200).send(product); res.status(200).send(product);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -65,7 +65,7 @@ productsRouter.get('/search/:term', async (req: Request, res: Response) => {
res.status(200).send(products); res.status(200).send(products);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -84,6 +84,6 @@ productsRouter.get('/list/:ids', async (req: Request, res: Response) => {
res.status(200).send(products); res.status(200).send(products);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -47,10 +47,13 @@ usersRouter.post('/register', async (req: Request, res: Response) => {
const session: Session = await UserService.createUser(username, password, email, ip); const session: Session = await UserService.createUser(username, password, email, ip);
// Send the session details back to the user // Send the session details back to the user
res.status(201).send(session); res.cookie('betterauth', JSON.stringify({
id: session.session_id,
key: session.session_key
}), {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)}).sendStatus(201);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -72,37 +75,32 @@ usersRouter.post('/login', async (req: Request, res: Response) => {
if (!session.session_id) { if (!session.session_id) {
// Error logging in, probably wrong username / password // Error logging in, probably wrong username / password
res.status(401).send(JSON.stringify({messages: ["Wrong username and / or password"], codes: [1, 4]})); res.status(401).send(JSON.stringify({messages: ['Wrong username and / or password'], codes: [1, 4]}));
return; return;
} }
// Send the session details back to the user // Send the session details back to the user
res.status(201).send(session); res.cookie('betterauth', JSON.stringify({
id: session.session_id,
key: session.session_key
}), {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)}).sendStatus(200);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
// POST users/checkSessionValid // POST users/checkSessionValid
usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => { usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
try { try {
const sessionId: string = req.body.sessionId;
const sessionKey: string = req.body.sessionKey;
const ip: string = req.connection.remoteAddress ?? ''; const ip: string = req.connection.remoteAddress ?? '';
if (!sessionId || !sessionKey) {
// Missing
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
return;
}
// Update the user entry and create a session // Update the user entry and create a session
const user: User = await UserService.checkSession(sessionId, sessionKey, ip); const user: User = await UserService.checkSessionWithCookie(req.cookies.betterauth, ip);
if (!user.user_id) { if (!user.user_id) {
// Error logging in, probably wrong username / password // Error logging in, probably wrong username / password
res.status(401).send(JSON.stringify({messages: ["Invalid session"], codes: [5]})); res.status(401).send(JSON.stringify({messages: ['Invalid session'], codes: [5]}));
return; return;
} }
@ -110,6 +108,6 @@ usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
res.status(201).send(user); res.status(201).send(user);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -68,7 +68,7 @@ export const createUser = async (username: string, password: string, email: stri
return { return {
session_id: sessionId, session_id: sessionId,
session_key: sessionKey, session_key: sessionKey,
session_key_hash: '', session_key_hash: 'HIDDEN',
last_IP: ip last_IP: ip
}; };
@ -135,7 +135,7 @@ export const login = async (username: string, password: string, ip: string): Pro
return { return {
session_id: sessionId, session_id: sessionId,
session_key: sessionKey, session_key: sessionKey,
session_key_hash: '', session_key_hash: 'HIDDEN',
last_IP: ip last_IP: ip
}; };
@ -193,7 +193,7 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
await conn.commit(); await conn.commit();
// Get the other required user information and update the user // Get the other required user information and update the user
const userQuery = "SELECT user_id, username, email, registration_date, last_login_date FROM users WHERE user_id = ?"; const userQuery = 'SELECT user_id, username, email, registration_date, last_login_date FROM users WHERE user_id = ?';
const userRows = await conn.query(userQuery, userId); const userRows = await conn.query(userQuery, userId);
let username = ''; let username = '';
let email = ''; let email = '';
@ -213,7 +213,7 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
user_id: userId, user_id: userId,
username: username, username: username,
email: email, email: email,
password_hash: '', password_hash: 'HIDDEN',
registration_date: registrationDate, registration_date: registrationDate,
last_login_date: lastLoginDate last_login_date: lastLoginDate
}; };
@ -229,6 +229,20 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
return {} as User; return {} as User;
}; };
/**
* Calls the checkSession method after extracting the required information from the authentication cookie
* @param cookie The betterauth cookie
* @param ip The users IP address
*/
export const checkSessionWithCookie = async (cookie: any, ip: string): Promise<User> => {
const parsedCookie = JSON.parse(cookie);
const session_id = parsedCookie.id;
const session_key = parsedCookie.key;
return checkSession(session_id, session_key, '');
};
/** /**
* Used in the checkUsernameAndEmail method as return value * Used in the checkUsernameAndEmail method as return value
*/ */

View File

@ -27,7 +27,7 @@ vendorsRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(vendors); res.status(200).send(vendors);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -46,7 +46,7 @@ vendorsRouter.get('/:id', async (req: Request, res: Response) => {
res.status(200).send(vendor); res.status(200).send(vendor);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });
@ -65,6 +65,6 @@ vendorsRouter.get('/search/:term', async (req: Request, res: Response) => {
res.status(200).send(vendors); res.status(200).send(vendors);
} catch (e) { } catch (e) {
console.log('Error handling a request: ' + e.message); console.log('Error handling a request: ' + e.message);
res.status(500).send(JSON.stringify({"message": "Internal Server Error. Try again later."})); res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
} }
}); });

View File

@ -1,32 +1,32 @@
const webpack = require("webpack"); const webpack = require('webpack');
const path = require("path"); const path = require('path');
const nodeExternals = require("webpack-node-externals"); const nodeExternals = require('webpack-node-externals');
module.exports = { module.exports = {
entry: ["webpack/hot/poll?100", "./src/index.ts"], entry: ['webpack/hot/poll?100', './src/index.ts'],
watch: false, watch: false,
target: "node", target: 'node',
externals: [ externals: [
nodeExternals({ nodeExternals({
whitelist: ["webpack/hot/poll?100"] whitelist: ['webpack/hot/poll?100']
}) })
], ],
module: { module: {
rules: [ rules: [
{ {
test: /.tsx?$/, test: /.tsx?$/,
use: "ts-loader", use: 'ts-loader',
exclude: /node_modules/ exclude: /node_modules/
} }
] ]
}, },
mode: "development", mode: 'development',
resolve: { resolve: {
extensions: [".tsx", ".ts", ".js"] extensions: ['.tsx', '.ts', '.js']
}, },
plugins: [new webpack.HotModuleReplacementPlugin()], plugins: [new webpack.HotModuleReplacementPlugin()],
output: { output: {
path: path.join(__dirname, "dist"), path: path.join(__dirname, 'dist'),
filename: "index.js" filename: 'index.js'
} }
}; };