fix(v2): OTP is now only digits, v3, adding Account APP

This commit is contained in:
Josepablo C
2024-04-18 10:36:10 -06:00
parent 3819508342
commit ab98e96e8b
5 changed files with 189 additions and 39 deletions

View File

@@ -9,25 +9,11 @@ function dummy_middleware( req, res ){
router.post('/register', dummy_middleware ); router.post('/register', dummy_middleware );
router.post('/authorize', dummy_middleware ); router.post('/authorize', async( req, res ) => {
router.get('/authorize/:session_token', dummy_middleware );
router.get('/check-account/:email', async( req, res ) => {
try{
const email = req.params.email;
const data = await Application.check_account( email );
return res.send( data );
}catch(error){
console.error( error );
}
} );
router.post('/signup', async(req,res) => {
try{ try{
const email = req.body.email; const email = req.body.email;
const password = req.body.password; const password = req.body.password;
const data = await Application.getVerifyChecksum( email , password ); const data = await Application.authorize_credentials( email , password );
if( data.error ){ if( data.error ){
const error = data.error; const error = data.error;
return res.status( error.code ).send( { error : error.msg } ); return res.status( error.code ).send( { error : error.msg } );
@@ -35,6 +21,53 @@ router.post('/signup', async(req,res) => {
return res.send( data ); return res.send( data );
}catch(error){ }catch(error){
console.error( error ); console.error( error );
return res.status( 500 ).send( { error } );
}
} );
router.get('/authorize/:session_token', async( req, res ) => {
try{
const session_token = req.params.session_token;
const data = await Application.authorize_token( session_token );
if( data.error ){
const error = data.error;
return res.status( error.code ).send( { error : error.msg } );
}
return res.send( data );
}catch(error){
console.error( error );
return res.status( 500 ).send( { error } );
}
} );
router.get('/check-account/:email', async( req, res ) => {
try{
const email = req.params.email;
const data = await Application.check_account( email );
if( data.error ){
const error = data.error;
return res.status( error.code ).send( { error : error.msg } );
}
return res.send( data );
}catch(error){
console.error( error );
return res.status( 500 ).send( { error } );
}
} );
router.post('/signup', async(req,res) => {
try{
const email = req.body.email;
const password = req.body.password;
const data = await Application.genOTPChecksum( email, password, "signup" );
if( data.error ){
const error = data.error;
return res.status( error.code ).send( { error : error.msg } );
}
return res.send( data );
}catch(error){
console.error( error );
return res.status( 500 ).send( { error } );
} }
} ); } );
router.patch('/signup', async(req,res) => { router.patch('/signup', async(req,res) => {
@@ -51,10 +84,41 @@ router.patch('/signup', async(req,res) => {
return res.send( data ); return res.send( data );
}catch(error){ }catch(error){
console.error( error ); console.error( error );
return res.status( 500 ).send( { error } );
} }
} ); } );
router.post('/recover', dummy_middleware ); router.post('/recover', async(req,res) => {
router.patch('/recover', dummy_middleware ); try{
const email = req.body.email;
const password = req.body.password;
const data = await Application.genOTPChecksum( email, password, "recover" );
if( data.error ){
const error = data.error;
return res.status( error.code ).send( { error : error.msg } );
}
return res.send( data );
}catch(error){
console.error( error );
return res.status( 500 ).send( { error } );
}
} );
router.patch('/recover', async(req,res) => {
try{
const email = req.body.email;
const password = req.body.password;
const otp = req.body.otp;
const checksum = req.body.checksum;
const data = await Application.recover( email , password, otp, checksum);
if( data.error ){
const error = data.error;
return res.status( error.code ).send( { error : error.msg } );
}
return res.send( data );
}catch(error){
console.error( error );
return res.status( 500 ).send( { error } );
}
} );
module.exports = router; module.exports = router;

View File

@@ -12,7 +12,7 @@ const { toSha256,
pwdSecret, pwdSecret,
genErrorResponse } = require('../Ports/Interfaces'); genErrorResponse } = require('../Ports/Interfaces');
class ApplicationLogic { class Account {
constructor(){ constructor(){
} }
@@ -50,13 +50,25 @@ class ApplicationLogic {
return retVal; return retVal;
} }
async getVerifyChecksum( email , password ){ async genOTPChecksum( email, password, reason="signup" ){
const otp = this.genOTP( email ); let event_id = reason;
const it_exists = await Repository.getByEmail( email );
if( reason === "signup" ){
event_id = "getchecksum:signup";
if( it_exists ){ const it_exists = await Repository.getByEmail( email );
return genErrorResponse( "Email already exists" ); if( it_exists ){
return genErrorResponse( "User already registered!" );
}
}else if( reason === "recover" ){
event_id = "getchecksum:recover";
}else{
return genErrorResponse( "OPT Event type not defined" , 500 );
} }
const otp = this.genOTP( email );
const content = { OTP : otp, user_name : email, email }; const content = { OTP : otp, user_name : email, email };
const checksum_entry = { const checksum_entry = {
@@ -66,7 +78,9 @@ class ApplicationLogic {
}; };
const checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32); const checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32);
publishEvent( "getchecksum" , content );
publishEvent( event_id , content );
return { checksum }; return { checksum };
} }
@@ -93,6 +107,25 @@ class ApplicationLogic {
return await this.authorize_credentials( email, password ); return await this.authorize_credentials( email, password );
} }
async recover( email, password, otp, checksum ){
const user = await Repository.getByEmail( email );
if( !user ){
return genErrorResponse( "Email is not registered!" );
}
const checksum_entry = {email, password, otp};
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32);
if( recomputed_checksum != checksum ){
return genErrorResponse( "Wrong OTP" );
}
await Repository.updatePassword( user.id, this.genSafePassword( password ) );
return await this.authorize_credentials( email, password );
}
async authorize_credentials( email, password ){ async authorize_credentials( email, password ){
const user = await Repository.findByEmailPassword( email, this.genSafePassword( password ) ); const user = await Repository.findByEmailPassword( email, this.genSafePassword( password ) );
@@ -128,6 +161,42 @@ class ApplicationLogic {
}; };
} }
async authorize_token( token ){
const user = await Repository.findBySessionToken( token );
if( !user ){
return genErrorResponse( "Invalid Session Token", 401 );
}
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 );
await Repository.addSessionToken( user.id, session_token, session_token_exp );
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
};
}
}; };
module.exports = new ApplicationLogic(); module.exports = new Account();

View File

@@ -1,14 +1,14 @@
'use strict'; 'use strict';
const { getModel } = require('../../../../Shared/Models/Objection'); const { getModel } = require('../../../../Shared/Models/Objection');
const Model = getModel('users'); const Users = getModel('users');
const Sessions = getModel('users_sessions'); const Sessions = getModel('users_sessions');
const Companies = getModel('companies'); const Companies = getModel('companies');
class SpecificModelRepository{ class SpecificModelRepository{
constructor(){} constructor(){}
async populateCompany( user ){ async populate( user ){
if( user.company_id ){ if( user.company_id ){
const company = await Companies.query().findById( user.company_id ); const company = await Companies.query().findById( user.company_id );
user.company = company; user.company = company;
@@ -19,28 +19,29 @@ class SpecificModelRepository{
} }
async getByEmail( email ){ async getByEmail( email ){
const user = await Model.query() const user = await Users.query()
.select( "*" ) .select( "*" )
.where("email","=",email).first(); .where("email","=",email).first();
return user;
} }
async createOne( email, safe_password ){ async createOne( email, safe_password ){
const user = await Model.query().insert({ const user = await Users.query().insert({
email, email,
password : safe_password, password : safe_password,
"name":"No name", "name":"No name",
"last_name":"No lastname", "last_name":"No lastname",
"createdAt" : new Date().toISOString() "createdAt" : new Date().toISOString()
}); });
return this.populateCompany( user ); return await this.populate( user );
} }
async findByEmailPassword( email, safe_password ){ async findByEmailPassword( email, safe_password ){
const user = await Model.query().select('*') const user = await Users.query().select('*')
.where("email","=",email) .where("email","=",email)
.where("password","=",safe_password) .where("password","=",safe_password)
.first(); .first();
return this.populateCompany( user ); return await this.populate( user );
} }
async updateSessionToken( old_token, token, expiration ){ async updateSessionToken( old_token, token, expiration ){
@@ -55,6 +56,12 @@ class SpecificModelRepository{
return null; return null;
} }
async updatePassword( userId , safe_password ){
return await Users.query()
.findById( userId )
.patch( { password : safe_password } );
}
async addSessionToken( userId , token, expiration ){ async addSessionToken( userId , token, expiration ){
const entry = await Sessions.query().select('*').where("user_id",'=',userId).first(); const entry = await Sessions.query().select('*').where("user_id",'=',userId).first();
const data = { const data = {
@@ -71,6 +78,12 @@ class SpecificModelRepository{
} }
} }
async findBySessionToken( token ){
const session = await Sessions.query().select("*").where("token","=",token).first();
const user = await Users.query().findById( session.user_id );
return await this.populate( user );
}
} }
module.exports = new SpecificModelRepository(); module.exports = new SpecificModelRepository();

View File

@@ -29,8 +29,8 @@ async function onContactFromWebPage( data ){
* Dictionary of event ids and handlers * Dictionary of event ids and handlers
*/ */
module.exports = { module.exports = {
"App:Account:getchecksum" : onChecksumGeneration, "App:Account:getchecksum:signup" : onChecksumGeneration,
"App:Account:signupconfirmed":onAccountConfirmed, "App:Account:signupconfirmed":onAccountConfirmed,
"App:Account:getchecksum:pwdreset":onPasswordReset, "App:Account:getchecksum:recover":onPasswordReset,
"App:ContactEmail:getchecksum":onContactFromWebPage, "App:ContactEmail:contact":onContactFromWebPage,
}; };

View File

@@ -13,6 +13,8 @@ const s3Client = new S3Client({
}); });
const secret = apiConfig.authentication.jwtSecret; const secret = apiConfig.authentication.jwtSecret;
const tokenSecret = apiConfig.authentication.tokenSecret;
/** /**
* Convert string to sha256 string in hex * Convert string to sha256 string in hex
* @param {*} text * @param {*} text
@@ -28,12 +30,14 @@ function toSha256( text ){
* @param {*} text * @param {*} text
* @returns * @returns
*/ */
function genKey( len = 6 , key="" ){ function genKey( len = 5 , key="" ){
if( len >= 64 ){ if( len >= 64 ){
throw "invalid key len"; throw "invalid key len";
} }
const complete_string = toSha256( key + new Date() + secret ); const shacode = toSha256( key + new Date() + tokenSecret );
return complete_string.substr(0 , len ); const otp_hex = shacode.slice(0 , len );
const otp_dec = Number.parseInt( otp_hex , 16 );
return ""+otp_dec;
} }
function getPagination( query ){ function getPagination( query ){