From a42c7da9a52f8509af145980bf665ba037b474e0 Mon Sep 17 00:00:00 2001 From: Patrick <50352812+Mueller-Patrick@users.noreply.github.com> Date: Mon, 3 May 2021 15:14:22 +0200 Subject: [PATCH] BETTERZON-79: Adding API endpoint for logging in (#36) --- Backend/src/models/users/users.router.ts | 31 +++++++++- Backend/src/models/users/users.service.ts | 73 ++++++++++++++++++++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/Backend/src/models/users/users.router.ts b/Backend/src/models/users/users.router.ts index 7499794..4dd4c92 100644 --- a/Backend/src/models/users/users.router.ts +++ b/Backend/src/models/users/users.router.ts @@ -26,7 +26,7 @@ usersRouter.post('/register', async (req: Request, res: Response) => { const username: string = req.body.username; const password: string = req.body.password; const email: string = req.body.email; - const ip: string = req.connection.remoteAddress?? ''; + const ip: string = req.connection.remoteAddress ?? ''; if (!username || !password || !email) { // Missing @@ -52,3 +52,32 @@ usersRouter.post('/register', async (req: Request, res: Response) => { res.status(404).send(e.message); } }); + +// POST users/login +usersRouter.post('/login', async (req: Request, res: Response) => { + try { + const username: string = req.body.username; + const password: string = req.body.password; + const ip: string = req.connection.remoteAddress ?? ''; + + if (!username || !password) { + // Missing + res.status(400).send(JSON.stringify({message: 'Missing parameters'})); + return; + } + + // Update the user entry and create a session + const session: Session = await UserService.login(username, password, ip); + + 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]})); + return; + } + + // Send the session details back to the user + res.status(201).send(session); + } catch (e) { + res.status(404).send(e.message); + } +}); diff --git a/Backend/src/models/users/users.service.ts b/Backend/src/models/users/users.service.ts index 1c66949..0501c4a 100644 --- a/Backend/src/models/users/users.service.ts +++ b/Backend/src/models/users/users.service.ts @@ -34,7 +34,7 @@ export const createUser = async (username: string, password: string, email: stri let conn; try { // Hash password and generate + hash session key - const pwHash = bcrypt.hashSync('123', 10); + const pwHash = bcrypt.hashSync(password, 10); const sessionKey = Guid.create().toString(); const sessionKeyHash = bcrypt.hashSync(sessionKey, 10); @@ -57,7 +57,74 @@ export const createUser = async (username: string, password: string, email: stri const sessionIdRes = await conn.query(sessionQuery, [userId, sessionKeyHash, ip]); await conn.commit(); - // Get session id of the created user + // Get session id of the created session + let sessionId: number = -1; + for (const row in sessionIdRes) { + if (row !== 'meta' && sessionIdRes[row].session_id != null) { + sessionId = sessionIdRes[row].session_id; + } + } + + return { + session_id: sessionId, + session_key: sessionKey, + session_key_hash: '', + last_IP: ip + }; + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return {} as Session; +}; + +/** + * Checks if the given credentials are valid and creates a new session if they are. + * Returns the session information in case of a successful login + */ +export const login = async (username: string, password: string, ip: string): Promise => { + let conn; + try { + // Get saved password hash + conn = await pool.getConnection(); + const query = "SELECT user_id, bcrypt_password_hash FROM users WHERE username = ?" + const userRows = await conn.query(query, username); + let savedHash = ''; + let userId = -1; + for (const row in userRows) { + if (row !== 'meta' && userRows[row].user_id != null) { + savedHash = userRows[row].bcrypt_password_hash; + userId = userRows[row].user_id; + } + } + + // Check for correct password + if(!bcrypt.compareSync(password, savedHash)) { + // Wrong password, return invalid + return {} as Session; + } + // Password is valid, continue + + // Generate + hash session key + const sessionKey = Guid.create().toString(); + const sessionKeyHash = bcrypt.hashSync(sessionKey, 10); + + // Update user entry in SQL + const userQuery = 'UPDATE users SET last_login_date = NOW()'; + const userIdRes = await conn.query(userQuery); + await conn.commit(); + + // Create session + const sessionQuery = 'INSERT INTO sessions (user_id, session_key_hash, createdDate, lastLogin, validUntil, validDays, last_IP) VALUES (?,?,NOW(),NOW(),DATE_ADD(NOW(), INTERVAL 30 DAY),30,?) RETURNING session_id'; + const sessionIdRes = await conn.query(sessionQuery, [userId, sessionKeyHash, ip]); + await conn.commit(); + + // Get session id of the created session let sessionId: number = -1; for (const row in sessionIdRes) { if (row !== 'meta' && sessionIdRes[row].session_id != null) { @@ -89,7 +156,7 @@ export const createUser = async (username: string, password: string, email: stri export interface Status { hasProblems: boolean; messages: string[]; - codes: number[]; // 0 = all good, 1 = wrong username, 2 = wrong email, 3 = server error + codes: number[]; // 0 = all good, 1 = wrong username, 2 = wrong email, 3 = server error, 4 = wrong password } /**