retropilot-server/src/server/controllers/authentication/index.js

126 lines
3.2 KiB
JavaScript

import crypto from 'crypto';
import jsonwebtoken from 'jsonwebtoken';
import log4js from 'log4js';
import { Accounts } from '../../../models';
const logger = log4js.getLogger();
export async function validateJWT(token, key) {
try {
return jsonwebtoken.verify(token.replace('JWT ', ''), key, { algorithms: ['RS256'], ignoreNotBefore: true });
} catch (exception) {
logger.warn(`failed to validate JWT ${exception}`);
}
return null;
}
export async function readJWT(token) {
try {
return jsonwebtoken.decode(token);
} catch (exception) {
logger.warn(`failed to read JWT ${exception}`);
}
return null;
}
async function signIn(email, password) {
let account = await Accounts.findOne({ where: { email } });
if (!account || !account.dataValues) {
return { success: false, msg: 'BAD ACCOUNT' };
}
account = account.dataValues;
const inputPassword = crypto.createHash('sha256').update(password + process.env.APP_SALT).digest('hex');
if (account.password !== inputPassword) {
return { success: false, msg: 'BAD PASSWORD' };
}
const token = jsonwebtoken.sign({ accountId: account.id }, process.env.APP_SALT);
return { success: true, jwt: token };
}
async function changePassword(account, newPassword, oldPassword) {
if (!account || !newPassword || !oldPassword) {
return { success: false, code: 400, error: 'MISSING_DATA' };
}
const oldPasswordHash = crypto.createHash('sha256').update(oldPassword + process.env.APP_SALT).digest('hex');
if (account.password !== oldPasswordHash) {
return { success: false, code: 400, msg: 'BAD_PASSWORD' };
}
const newPasswordHash = crypto.createHash('sha256').update(newPassword + process.env.APP_SALT).digest('hex');
await Accounts.update(
{ password: newPasswordHash },
{ where: { id: account.id } },
);
return { success: true, msg: 'PASSWORD_CHANGED' };
}
/*
TODO: update rest of the code to support authentication rejection reasons
*/
async function getAuthenticatedAccount(req) {
const sessionJWT = req.cookies.jwt;
if ((!sessionJWT || sessionJWT.expires <= Date.now())) {
return null;
}
return getAccountFromJWT(sessionJWT);
}
async function getAccountFromJWT(jwt, limitData = true) {
let token;
try {
token = jsonwebtoken.verify(jwt, process.env.APP_SALT);
} catch (err) {
return null;// {success: false, msg: 'BAD_JWT'}
}
if (!token || !token.accountId) {
return null; // {success: false, badToken: true}
}
let query = { where: { id: token.accountId } };
if (limitData) {
// we don't want to include sensitive info in the response
query = {
...query,
attributes: { exclude: ['password', '2fa_token', 'session_seed'] },
};
}
const account = await Accounts.findOne(query);
if (!account || !account.dataValues) {
return null; // {success: false, isInvalid: true}
}
try {
await Accounts.update(
{ last_ping: Date.now() },
{ where: { id: account.id } },
);
} catch (error) {
console.log(error);
}
if (!account || account.banned) {
return null; // {success: false, isBanned: true}
}
return account;
}
export default {
validateJWT,
getAuthenticatedAccount,
changePassword,
signIn,
readJWT,
getAccountFromJWT,
};