init
This commit is contained in:
94
authorization/auth.controller.js
Normal file
94
authorization/auth.controller.js
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
const crypto = require('crypto');
|
||||
const config = require('../config.js');
|
||||
const jwtSecret = config.jwt_secret;
|
||||
const UserModel = require('../users/users.model');
|
||||
const UserTool = require('../users/users.tool');
|
||||
|
||||
exports.Login = (req, res) => {
|
||||
try {
|
||||
|
||||
let refreshId = req.login.userId + jwtSecret;
|
||||
let refresh_key = crypto.randomBytes(16).toString('base64');
|
||||
let refresh_hash = crypto.createHmac('sha512', refresh_key).update(refreshId).digest("base64");
|
||||
req.login.refresh_key = refresh_key;
|
||||
|
||||
let access_token = jwt.sign(req.login, jwtSecret);
|
||||
|
||||
//Delete some keys for security, empty keys are never valid, also update login time
|
||||
var update = {refresh_key: refresh_key, proof_key: "", password_recovery_key: "", last_login_time: new Date(), last_online_time: new Date()};
|
||||
UserModel.patch(req.login.userId, update);
|
||||
|
||||
var odata = {
|
||||
id: req.login.userId,
|
||||
username: req.login.username,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_hash,
|
||||
permission_level: req.login.permission_level,
|
||||
validation_level: req.login.validation_level,
|
||||
duration: config.jwt_expiration,
|
||||
server_time: new Date(),
|
||||
version: config.version
|
||||
}
|
||||
|
||||
return res.status(201).send(odata);
|
||||
|
||||
} catch (err) {
|
||||
return res.status(500).send({error: err});
|
||||
}
|
||||
};
|
||||
|
||||
exports.KeepOnline = async(req, res, next) => {
|
||||
|
||||
var token = req.jwt;
|
||||
UserModel.patch(token.userId, {last_online_time: new Date()});
|
||||
|
||||
var data = {id: token.userId, username: token.username, login_time: new Date(token.iat * 1000), server_time: new Date() };
|
||||
return res.status(200).send(data);
|
||||
};
|
||||
|
||||
exports.GetVersion = (req, res) =>{
|
||||
return res.status(200).send({version: config.version});
|
||||
};
|
||||
|
||||
// ----- verify user -----------
|
||||
|
||||
exports.ValidateToken = async(req, res, next) => {
|
||||
|
||||
var token = req.jwt;
|
||||
var data = {id: token.userId, username: token.username, login_time: new Date(token.iat * 1000), server_time: new Date() };
|
||||
return res.status(200).send(data);
|
||||
};
|
||||
|
||||
exports.CreateProof = async(req, res) =>
|
||||
{
|
||||
var userId = req.jwt.userId;
|
||||
|
||||
var user = await UserModel.getById(userId);
|
||||
if(!user)
|
||||
return res.status(404).send({error: "User not found"});
|
||||
|
||||
user.proof_key = crypto.randomBytes(20).toString('base64');
|
||||
await UserModel.save(user);
|
||||
|
||||
return res.status(200).send({proof: user.proof_key});
|
||||
}
|
||||
|
||||
exports.ValidateProof = async(req, res) =>
|
||||
{
|
||||
var username = req.params.username;
|
||||
var proof = req.params.proof;
|
||||
|
||||
if(!username || typeof username != "string" || !proof || typeof proof != "string")
|
||||
return res.status(400).send({error: "Invalid parameters"});
|
||||
|
||||
var user = await UserModel.getByUsername(username);
|
||||
if(!user)
|
||||
return res.status(404).send({error: "User not found"});
|
||||
|
||||
if(!user.proof_key || user.proof_key != proof)
|
||||
return res.status(403).send({error: "Invalid Proof"});
|
||||
|
||||
return res.status(200).send();
|
||||
}
|
||||
44
authorization/auth.routes.js
Normal file
44
authorization/auth.routes.js
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
const AuthController = require('./auth.controller');
|
||||
const AuthTool = require('./auth.tool');
|
||||
|
||||
exports.route = function (app) {
|
||||
|
||||
//Body: username, password
|
||||
app.post('/auth', app.auth_limiter, [
|
||||
AuthTool.isLoginValid,
|
||||
AuthController.Login
|
||||
]);
|
||||
|
||||
//Body: refresh_token
|
||||
app.post('/auth/refresh', app.auth_limiter, [
|
||||
AuthTool.isRefreshValid,
|
||||
AuthController.Login
|
||||
]);
|
||||
|
||||
app.get('/auth/keep',[
|
||||
AuthTool.isValidJWT,
|
||||
AuthController.KeepOnline
|
||||
]);
|
||||
|
||||
app.get('/auth/validate',[
|
||||
AuthTool.isValidJWT,
|
||||
AuthController.ValidateToken
|
||||
]);
|
||||
|
||||
app.get("/auth/proof/create", [
|
||||
AuthTool.isValidJWT,
|
||||
AuthController.CreateProof
|
||||
]);
|
||||
|
||||
app.get("/auth/proof/:username/:proof", [
|
||||
AuthTool.isValidJWT,
|
||||
AuthController.ValidateProof
|
||||
]);
|
||||
|
||||
app.get('/version', [
|
||||
AuthController.GetVersion
|
||||
]);
|
||||
|
||||
|
||||
};
|
||||
162
authorization/auth.tool.js
Normal file
162
authorization/auth.tool.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const config = require('../config.js');
|
||||
const UserModel = require('../users/users.model');
|
||||
|
||||
//-----Validations------
|
||||
|
||||
var AuthTool = {};
|
||||
|
||||
|
||||
AuthTool.isValidJWT = (req, res, next) => {
|
||||
|
||||
if (!req.headers['authorization'])
|
||||
return res.status(401).send();
|
||||
|
||||
try {
|
||||
//Validate access token
|
||||
let authorization = req.headers['authorization'];
|
||||
req.jwt = jwt.verify(authorization, config.jwt_secret);
|
||||
|
||||
//Validate expiry time
|
||||
const nowSeconds = Math.round(Number(new Date()) / 1000);
|
||||
const expiration = req.jwt.iat + config.jwt_expiration;
|
||||
if(nowSeconds > expiration)
|
||||
return res.status(403).send({error: "Expired"});
|
||||
|
||||
} catch (err) {
|
||||
return res.status(403).send({error: "Invalid Token"});
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
AuthTool.isLoginValid = async(req, res, next) => {
|
||||
|
||||
if (!req.body || !req.body.password)
|
||||
return res.status(400).send({error: 'Invalid params'});
|
||||
|
||||
//Requires EITHER username or email, dont need both
|
||||
if (!req.body.email && !req.body.username)
|
||||
return res.status(400).send({error: 'Invalid params'});
|
||||
|
||||
var user = null;
|
||||
|
||||
if(req.body.email)
|
||||
user = await UserModel.getByEmail(req.body.email);
|
||||
else if(req.body.username)
|
||||
user = await UserModel.getByUsername(req.body.username);
|
||||
if(!user)
|
||||
return res.status(404).send({error: "Invalid username or password"});
|
||||
|
||||
let validPass = AuthTool.validatePassword(user, req.body.password);
|
||||
if(!validPass)
|
||||
return res.status(400).send({error: 'Invalid username or password'});
|
||||
|
||||
if(user.permission_level <= 0)
|
||||
return res.status(403).send({error: "Your account has been disabled, please contact support!"});
|
||||
|
||||
req.login = {
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
permission_level: user.permission_level,
|
||||
validation_level: user.validation_level,
|
||||
provider: req.body.email ? 'email' : 'username',
|
||||
};
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
AuthTool.isRefreshValid = async(req, res, next) => {
|
||||
|
||||
if (!req.body || !req.body.refresh_token)
|
||||
return res.status(400).send();
|
||||
|
||||
if (!req.headers['authorization'])
|
||||
return res.status(401).send();
|
||||
|
||||
if (typeof req.body.refresh_token !== "string")
|
||||
return res.status(400).send();
|
||||
|
||||
try {
|
||||
//Validate access token
|
||||
let authorization = req.headers['authorization'];
|
||||
req.jwt = jwt.verify(authorization, config.jwt_secret);
|
||||
|
||||
//Validate expiry time
|
||||
const nowUnixSeconds = Math.round(Number(new Date()) / 1000);
|
||||
const expiration = req.jwt.iat + config.jwt_refresh_expiration;
|
||||
if(nowUnixSeconds > expiration)
|
||||
return res.status(403).send({error: "Token Expired"});
|
||||
|
||||
//Validate refresh token
|
||||
let refresh_token = req.body.refresh_token;
|
||||
let hash = crypto.createHmac('sha512', req.jwt.refresh_key).update(req.jwt.userId + config.jwt_secret).digest("base64");
|
||||
if (hash !== refresh_token)
|
||||
return res.status(403).send({error: 'Invalid refresh token'});
|
||||
|
||||
//Validate refresh key in DB
|
||||
var user = await UserModel.getById(req.jwt.userId);
|
||||
if(!user)
|
||||
return res.status(404).send({error: "Invalid user"});
|
||||
|
||||
if(user.refresh_key !== req.jwt.refresh_key)
|
||||
return res.status(403).send({error: 'Invalid refresh key'});
|
||||
|
||||
} catch (err) {
|
||||
return res.status(403).send({error: "Invalid Token"});
|
||||
}
|
||||
|
||||
req.login = req.jwt;
|
||||
delete req.login.iat; //Delete previous iat to generate a new one
|
||||
return next();
|
||||
};
|
||||
|
||||
AuthTool.hashPassword = (password) => {
|
||||
let saltNew = crypto.randomBytes(16).toString('base64');
|
||||
let hashNew = crypto.createHmac('sha512', saltNew).update(password).digest("base64");
|
||||
let newPass = saltNew + "$" + hashNew;
|
||||
return newPass;
|
||||
}
|
||||
|
||||
AuthTool.validatePassword = (user, password) =>
|
||||
{
|
||||
let passwordFields = user.password.split('$');
|
||||
let salt = passwordFields[0];
|
||||
let hash = crypto.createHmac('sha512', salt).update(password).digest("base64");
|
||||
return hash === passwordFields[1];
|
||||
}
|
||||
|
||||
//--- Permisions -----
|
||||
|
||||
AuthTool.isPermissionLevel = (required_permission) => {
|
||||
return (req, res, next) => {
|
||||
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||
if (user_permission_level >= required_permission) {
|
||||
return next();
|
||||
} else {
|
||||
return res.status(403).send({error: "Permission Denied"});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
AuthTool.isSameUserOr = (required_permission) => {
|
||||
return (req, res, next) => {
|
||||
let user_permission_level = parseInt(req.jwt.permission_level);
|
||||
let userId = req.params.userId || "";
|
||||
let same_user = (req.jwt.userId === userId || req.jwt.username.toLowerCase() === userId.toLowerCase());
|
||||
if (userId && same_user) {
|
||||
return next();
|
||||
} else {
|
||||
if (user_permission_level >= required_permission) {
|
||||
return next();
|
||||
} else {
|
||||
return res.status(403).send({error: "Permission Denied"});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = AuthTool;
|
||||
Reference in New Issue
Block a user