"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( '../../../lib/Misc' ); const { emailEvent , EMAIL_EVENTS } = require( '../../../lib/Handlers/MailClient' ); const { create_account, already_exists, verify_driver_account, login, login_with_session_token, reset_password } = require( '../../../lib/Handlers/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 }, 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', '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 }; async function AuthorizeJWT_email_pwd( email , password ){ const user = await login( email, password ); if( !user ){ return null; } 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 { accessToken : jwt, payload : payload, session_token, session_token_exp, user : user }; } const AuthorizeJWT = async(req, res) => { try{ if( validator.validate( req.body , login_account_schema ).valid ){ const { email, password } = req.body; const retVal = await AuthorizeJWT_email_pwd( email , password ); if( !retVal ){ return res.status(401).send( { error : "Invalid credentials" } ); } return res.send( retVal ); }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, 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 ); const content = { user_name : email }; const receiver = email; await emailEvent( EMAIL_EVENTS.ACCOUNT_CONFIRMED , receiver , content ); const retVal = await AuthorizeJWT_email_pwd( email , password ); return res.send( retVal ); }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_PWD_RESET , 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" }); } }; const checkAccount = async(req, res) => { try{ const email = req.params.email; const driver_account_val = await verify_driver_account( email ); return res.status(200).send( driver_account_val ); } catch ( err ){ console.error( err ); return res.status(500).send({ error : "AuthManagement: Internal error" }); } }; module.exports = { AuthorizeJWT, RenewJWT, TryCreateAccount, ConfirmAccount, RecoverPwd, ConfirmRecoverPwd, checkAccount};