Files
ETAApi/src/apps/public/account/services.js

264 lines
9.7 KiB
JavaScript

"use strict";
const jsonwebtoken = require('jsonwebtoken');
const { API_CONFIG, ROOT_PATH, LIB_PATH, HANDLERS_PATH } = process.env;
const apiConfig = require( `${ROOT_PATH}/${API_CONFIG}` );
const { genKey, toSha256 } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` );
const { emailEvent , EMAIL_EVENTS } = require( `${ROOT_PATH}/${HANDLERS_PATH}/MailClient` );
const { create_account, already_exists, login, login_with_session_token, reset_password } = require( `${ROOT_PATH}/${HANDLERS_PATH}/Account` );
const { Validator } = require( "jsonschema" );
const jwtSecret = apiConfig.authentication.jwtSecret;
const jwtTimeout = apiConfig.authentication.jwtTimeout;//Timeout in hours
const jwtRenewalTimeout = apiConfig.authentication.jwtRenewalTimeout;//Timeout in hours
const jwtOptions = apiConfig.authentication.jwtOptions;
const validator = new Validator();
const create_account_schema = {
type : 'object',
properties : {
email : { type : 'string' , maxLength : 256 },
password : { type : 'string', maxLength : 256},
otp : { type : 'string', maxLength : 6 },
company_type : { type : 'string', enum : ["Shipper" , "Carrier"] },
checksum : { type : 'string', maxLength : 32 }
},
required : [ 'email', 'password' ]
};
const confirm_account_schema = {
type : 'object',
properties : create_account_schema.properties,//Same properties
required : [ 'email', 'password', 'otp', 'company_type', 'checksum' ]//Different requirements
};
const login_account_schema = {
type : 'object',
properties : create_account_schema.properties,//Same properties
required : [ 'email', 'password' ]//Different requirements
};
const password_recover_schema = create_account_schema;
const confirm_password_recover_schema = {
type : 'object',
properties : create_account_schema.properties,//Same properties
required : [ 'email', 'password', 'otp', 'checksum' ]//Different requirements
};
const AuthorizeJWT = async(req, res) => {
try{
if( validator.validate( req.body , login_account_schema ).valid ){
const { email, password } = req.body;
const user = await login( email, password );
if( !user ){
return res.status(401).send( { error : "Invalid credentials" } );
}
const current_date = new Date();
const iat = Math.floor( (current_date.getTime())/1000 );
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
/**
* Renew session token on every login event.
* Previous session token is lost
*/
const session_token = toSha256( `${new Date()}` );
const session_token_exp = new Date( renewal_exp );
user.session_token = session_token;
user.session_token_exp = session_token_exp;
await user.save();
const payload = {
iat: iat,
exp: iat + jwtTimeout * 3600,
aud: jwtOptions.audience,
iss: jwtOptions.audience,
sub: user.id,
};
const jwt = jsonwebtoken.sign( payload , jwtSecret );
return res.status(200).send( {
accessToken : jwt,
payload : payload,
session_token,
session_token_exp,
user : user
} );
}else{
return res.status(400).send( { error : "Invalid request" } );
}
}catch( err ){
console.error( err );
res.status(500).send({ error : "Login: Internal error" });
}
};
const RenewJWT = async(req, res) => {
try{
const login_session_token = req.params.session_token;
const user = await login_with_session_token( login_session_token );
if( !user ){
return res.status(401).send( { error : "Invalid or Expired Session Token" } );
}
const current_date = new Date();
const iat = Math.floor( (current_date.getTime())/1000 );
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
/**
* Renew session token on every login event.
* Previous session token is lost
*/
const session_token = toSha256( `${new Date()}` );
const session_token_exp = new Date( renewal_exp );
user.session_token = session_token;
user.session_token_exp = session_token_exp;
await user.save();
const payload = {
iat: iat,
exp: iat + jwtTimeout * 3600,
aud: jwtOptions.audience,
iss: jwtOptions.audience,
sub: user.id,
};
const jwt = jsonwebtoken.sign( payload , jwtSecret );
return res.status(200).send( {
accessToken : jwt,
payload : payload,
session_token,
session_token_exp,
user : user
} );
}catch( err ){
console.error( err );
res.status(500).send({ error : "Renew: Internal error" });
}
};
const TryCreateAccount = async(req, res) => {
try{
if( validator.validate( req.body , create_account_schema ).valid ){
const otp = genKey();
const { email : receiver , password } = req.body;
const email = receiver;
const it_exists = await already_exists( email );
if( it_exists ){
return res.status(400).send({ error : "Email already exists" });
}
const content = { OTP : otp, user_name : email };
const checksum_entry = {
email : receiver,
password,
otp
};
const checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
await emailEvent( EMAIL_EVENTS.ACCOUNT_VERIFY , receiver , content );
console.log(
content
);
res.status(200).send( { checksum } );
}else{
res.status(400).send( { error : "Invalid request" } );
}
}catch( err ){
console.error( err );
res.status(500).send({ error : "Account creation: Internal error" });
}
};
const ConfirmAccount = async(req, res) => {
try{
if( validator.validate( req.body , confirm_account_schema ).valid ){
const { email, password, otp, company_type, checksum } = req.body;
const it_exists = await already_exists( email );
if( it_exists ){
return res.status(400).send({ error : "User already registered!" });
}
const checksum_entry = {email, password, otp};
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
if( recomputed_checksum != checksum ){
return res.status(400).send({ error : "Wrong OTP" });
}
await create_account( email, password, company_type );
const content = { user_name : email };
const receiver = email;
// await emailEvent( EMAIL_EVENTS.ACCOUNT_CONFIRMED , receiver , content );
console.log(
content
);
return res.status(200).send( { msg : "User created successfully!" } );
}else{
return res.status(400).send( { error : "Invalid request" } );
}
}catch( err ){
console.error( err );
return res.status(500).send({ error : "Account creation: Internal error" });
}
};
const RecoverPwd = async(req, res) => {
try{
if( validator.validate( req.body , password_recover_schema ).valid ){
const otp = genKey();
const { email : receiver , password } = req.body;
const email = receiver;
const it_exists = await already_exists( email );
if( !it_exists ){
return res.status(400).send({ error : "Email is not registered!" });
}
const content = { OTP : otp, user_name : email };
const checksum_entry = {
email : receiver,
password,
otp
};
const checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
await emailEvent( EMAIL_EVENTS.ACCOUNT_VERIFY , receiver , content );
console.log(
content
);
res.status(200).send( { checksum } );
}else{
res.status(400).send( { error : "Invalid request" } );
}
}catch( err ){
console.error( err );
res.status(500).send({ error : "Password Recover: Internal error" });
}
};
const ConfirmRecoverPwd = async(req, res) => {
try{
if( validator.validate( req.body , confirm_password_recover_schema ).valid ){
const { email, password, otp, checksum } = req.body;
const it_exists = await already_exists( email );
if( !it_exists ){
return res.status(400).send({ error : "Email is not registered!" });
}
const checksum_entry = {email, password, otp};
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).substr(0, 32);
if( recomputed_checksum != checksum ){
return res.status(400).send({ error : "Wrong OTP" });
}
await reset_password( email, password );
return res.status(200).send( { msg : "Password is reset!" } );
}else{
return res.status(400).send( { error : "Invalid request" } );
}
}catch( err ){
console.error( err );
return res.status(500).send({ error : "Password Recover Confirmation: Internal error" });
}
};
module.exports = { AuthorizeJWT, RenewJWT, TryCreateAccount, ConfirmAccount, RecoverPwd, ConfirmRecoverPwd};