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": "",
"license": "ISC",
"dependencies": {
"@types/cookie-parser": "^1.4.2",
"bcrypt": "^5.0.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",

View File

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

View File

@ -1,5 +1,5 @@
import HttpException from "../common/http-exception";
import { Request, Response, NextFunction } from "express";
import HttpException from '../common/http-exception';
import {Request, Response, NextFunction} from 'express';
export const errorHandler = (
error: HttpException,
@ -9,7 +9,7 @@ export const errorHandler = (
) => {
const status = error.statusCode || 500;
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);
};

View File

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

View File

@ -27,7 +27,7 @@ categoriesRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(categories);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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) => {
try {
// Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? '';
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 user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
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) => {
try {
// Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? '';
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 user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
// Get info for price alarm creation
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) => {
try {
// Authenticate user
const session_id = req.body.session_id;
const session_key = req.body.session_key;
const user_ip = req.connection.remoteAddress ?? '';
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 user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
// Get info for price alarm creation
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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);
// 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) {
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.'}));
}
});
@ -70,39 +73,34 @@ usersRouter.post('/login', async (req: Request, res: Response) => {
// Update the user entry and create a session
const session: Session = await UserService.login(username, password, ip);
if(!session.session_id) {
if (!session.session_id) {
// 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;
}
// 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) {
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
usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
try {
const sessionId: string = req.body.sessionId;
const sessionKey: string = req.body.sessionKey;
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
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
res.status(401).send(JSON.stringify({messages: ["Invalid session"], codes: [5]}));
res.status(401).send(JSON.stringify({messages: ['Invalid session'], codes: [5]}));
return;
}
@ -110,6 +108,6 @@ usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
res.status(201).send(user);
} catch (e) {
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 {
session_id: sessionId,
session_key: sessionKey,
session_key_hash: '',
session_key_hash: 'HIDDEN',
last_IP: ip
};
@ -135,7 +135,7 @@ export const login = async (username: string, password: string, ip: string): Pro
return {
session_id: sessionId,
session_key: sessionKey,
session_key_hash: '',
session_key_hash: 'HIDDEN',
last_IP: ip
};
@ -179,7 +179,7 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
// Key is valid, continue
// Check if the session is still valid
if(validUntil <= new Date()) {
if (validUntil <= new Date()) {
// Session expired, return invalid
return {} as User;
}
@ -193,7 +193,7 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
await conn.commit();
// 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);
let username = '';
let email = '';
@ -213,7 +213,7 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
user_id: userId,
username: username,
email: email,
password_hash: '',
password_hash: 'HIDDEN',
registration_date: registrationDate,
last_login_date: lastLoginDate
};
@ -229,6 +229,20 @@ export const checkSession = async (sessionId: string, sessionKey: string, ip: st
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
*/

View File

@ -27,7 +27,7 @@ vendorsRouter.get('/', async (req: Request, res: Response) => {
res.status(200).send(vendors);
} catch (e) {
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);
} catch (e) {
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);
} catch (e) {
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 path = require("path");
const nodeExternals = require("webpack-node-externals");
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: ["webpack/hot/poll?100", "./src/index.ts"],
entry: ['webpack/hot/poll?100', './src/index.ts'],
watch: false,
target: "node",
target: 'node',
externals: [
nodeExternals({
whitelist: ["webpack/hot/poll?100"]
whitelist: ['webpack/hot/poll?100']
})
],
module: {
rules: [
{
test: /.tsx?$/,
use: "ts-loader",
use: 'ts-loader',
exclude: /node_modules/
}
]
},
mode: "development",
mode: 'development',
resolve: {
extensions: [".tsx", ".ts", ".js"]
extensions: ['.tsx', '.ts', '.js']
},
plugins: [new webpack.HotModuleReplacementPlugin()],
output: {
path: path.join(__dirname, "dist"),
filename: "index.js"
path: path.join(__dirname, 'dist'),
filename: 'index.js'
}
};