From: charles Date: Thu, 18 Sep 2025 15:03:27 +0000 (+0000) Subject: Fixing auth with cookies. X-Git-Url: https://git.phasecustomsoft.com/static/gitweb.js?a=commitdiff_plain;h=1c5225472a9d5b1921c1c0445c3713f6b4574475;p=phs-api.git Fixing auth with cookies. --- diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..6df4889 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index bac979d..8549cb1 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -4,10 +4,25 @@ const db = require( '../models' ); const createError = require( 'http-errors' ); +const jwt = require('jsonwebtoken'); +const config = require('../config/default.json'); +const phs_env = process.env.PHS_ENV; const updateUserAndReturn = async ( validUser, res ) => { const token = await validUser.createToken(); - res.status( 200 ).send( { success:true, user:validUser.to_safe_json(), token } ); + await setCookie('phase_request_token', { user_id: validUser.id, request_token: token }, res); + res.status( 200 ).send( { success:true, user:validUser, token } ); +}; + +const setCookie = (cookieKey, payload, res) => { + const token = jwt.sign(payload, config.keys.secret2fa, { algorithm: 'HS256', expiresIn: '1y' }); + res.cookie(cookieKey, token, { + maxAge: 31536000000, + httpOnly: true, + secure: (phs_env !== 'DEV'), + sameSite: 'lax', + domain: '.phasecustomsoft.com' + }); // expire in 1 year: 31536000000 }; module.exports = { diff --git a/src/controllers/authentication.controller.js b/src/controllers/authentication.controller.js index 5838010..7402fdb 100644 --- a/src/controllers/authentication.controller.js +++ b/src/controllers/authentication.controller.js @@ -21,7 +21,7 @@ module.exports = { try { const auth_data = req.body; const auth = await db.authentication.create( auth_data ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Create authentication error: ${ error.message }` ); next( createError( error.status || 400, error.message ) ); @@ -40,7 +40,7 @@ module.exports = { const { user_id } = req.params; const auth = await db.authentication.find_by_user_id( parseInt( user_id ) ); if (!auth) return next( createError( 404, 'Authentication record not found' ) ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Find authentication by user ID error: ${ error.message }` ); next( createError( error.status || 500, error.message ) ); @@ -59,7 +59,7 @@ module.exports = { const { token } = req.params; const auth = await db.authentication.find_by_reset_token( token ); if (!auth) return next( createError( 404, 'Authentication record not found' ) ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Find authentication by reset token error: ${ error.message }` ); next( createError( error.status || 500, error.message ) ); @@ -78,7 +78,7 @@ module.exports = { const { id } = req.params; const auth = await db.authentication.find_one( { id:parseInt( id ) } ); if (!auth) return next( createError( 404, 'Authentication record not found' ) ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Find authentication error: ${ error.message }` ); next( createError( error.status || 500, error.message ) ); @@ -96,7 +96,7 @@ module.exports = { try { const { limit = '100', offset = '0', ...where } = req.query; const auths = await db.authentication.find_many( where, [], null, parseInt( limit ), parseInt( offset ) ); - res.json( auths.map( auth => auth.to_safe_json() ) ); + res.json( auths ); } catch (error) { logger.error( `Find many authentication records error: ${ error.message }` ); next( createError( error.status || 500, error.message ) ); @@ -114,7 +114,7 @@ module.exports = { try { const { id } = req.params; const auth = await db.authentication.lock_account( id ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Lock account error: ${ error.message }` ); next( createError( error.status || 400, error.message ) ); @@ -132,7 +132,7 @@ module.exports = { try { const { id } = req.params; const auth = await db.authentication.unlock_account( id ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Unlock account error: ${ error.message }` ); next( createError( error.status || 400, error.message ) ); @@ -151,7 +151,7 @@ module.exports = { const { id } = req.params; const { deleted_by_id } = req.body; const auth = await db.authentication.soft_delete( id, deleted_by_id ); - res.json( auth.to_safe_json() ); + res.json( auth ); } catch (error) { logger.error( `Soft delete authentication error: ${ error.message }` ); next( createError( error.status || 400, error.message ) ); diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index feb284c..cfa9418 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -2,8 +2,8 @@ * @file User controller for handling user-related API requests */ -const db = require('../models'); -const createError = require('http-errors'); +const db = require( '../models' ); +const createError = require( 'http-errors' ); /** * User controller @@ -17,14 +17,14 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async create(req, res, next) { + async create( req, res, next ) { try { const user_data = req.body; - const user = await db.user.create(user_data); - res.status(200).send(user.to_safe_json()); + const user = await db.user.create( user_data ); + res.status( 200 ).send( user ); } catch (error) { - logger.error(`Create user error: ${error.message}`); - next(createError(error.status || 400, error.message)); + logger.error( `Create user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); } }, @@ -35,15 +35,15 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async find_one(req, res, next) { + async find_one( req, res, next ) { try { const { id } = req.params; - const user = await db.user.find_one({ id: parseInt(id) }); - if (!user) return next(createError(404, 'User not found')); - res.status(200).send(user.to_safe_json()); + const user = await db.user.find_one( { id:parseInt( id ) } ); + if (!user) return next( createError( 404, 'User not found' ) ); + res.status( 200 ).send( user ); } catch (error) { - logger.error(`Show user error: ${error.message}`); - next(createError(error.status || 500, error.message)); + logger.error( `Show user error: ${ error.message }` ); + next( createError( error.status || 500, error.message ) ); } }, @@ -54,15 +54,15 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async find_many(req, res, next) { + async find_many( req, res, next ) { try { - const { limit = 100, offset = 0, ...where } = req.query; - const users = await db.user.find_many(where, [], null, parseInt(limit), parseInt(offset)); - if (!users.length) return next(createError(404, 'No users found')); - res.status(200).send(users.map(u => u.to_safe_json())); + const { limit = '100', offset = '0', ...where } = req.query; + const users = await db.user.find_many( where, [], null, parseInt( limit ), parseInt( offset ) ); + if (!users.length) return next( createError( 404, 'No users found' ) ); + res.status( 200 ).send( users.map( u => new (require( '../models/user.model' )( u ).toJSON()) ) ); } catch (error) { - logger.error(`Index users error: ${error.message}`); - next(createError(error.status || 500, error.message)); + logger.error( `Index users error: ${ error.message }` ); + next( createError( error.status || 500, error.message ) ); } }, @@ -73,15 +73,15 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async find_by_email(req, res, next) { + async find_by_email( req, res, next ) { try { const { email } = req.params; - const user = await db.user.find_by_email(email); - if (!user) return next(createError(404, 'User not found')); - res.status(200).send(user.to_safe_json()); + const user = await db.user.find_by_email( email ); + if (!user) return next( createError( 404, 'User not found' ) ); + res.status( 200 ).send( user ); } catch (error) { - logger.error(`Find user by email error: ${error.message}`); - next(createError(error.status || 500, error.message)); + logger.error( `Find user by email error: ${ error.message }` ); + next( createError( error.status || 500, error.message ) ); } }, @@ -92,15 +92,15 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async find_by_nickname(req, res, next) { + async find_by_nickname( req, res, next ) { try { const { nickname } = req.params; - const user = await db.user.find_by_nickname(nickname); - if (!user) return next(createError(404, 'User not found')); - res.status(200).send(user.to_safe_json()); + const user = await db.user.find_by_nickname( nickname ); + if (!user) return next( createError( 404, 'User not found' ) ); + res.status( 200 ).send( user ); } catch (error) { - logger.error(`Find user by nickname error: ${error.message}`); - next(createError(error.status || 500, error.message)); + logger.error( `Find user by nickname error: ${ error.message }` ); + next( createError( error.status || 500, error.message ) ); } }, @@ -111,17 +111,17 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async update(req, res, next) { + async update( req, res, next ) { try { const { id } = req.params; const user_data = req.body; - const user = await db.user.instance().find_one({ id: parseInt(id) }); - if (!user) return next(createError(404, 'User not found')); - const updated_user = await user.update(user_data); - res.status(200).send(updated_user.to_safe_json()); + const user = await db.user.instance().find_one( { id:parseInt( id ) } ); + if (!user) return next( createError( 404, 'User not found' ) ); + const updated_user = await user.update( user_data ); + res.status( 200 ).send( updated_user ); } catch (error) { - logger.error(`Update user error: ${error.message}`); - next(createError(error.status || 400, error.message)); + logger.error( `Update user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); } }, @@ -132,17 +132,17 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async deactivate(req, res, next) { + async deactivate( req, res, next ) { try { const { id } = req.params; const { deactivated_by_id } = req.body; - const user = await db.user.instance().find_one({ id: parseInt(id) }); - if (!user) return next(createError(404, 'User not found')); - const deactivated_user = await user.deactivate(deactivated_by_id); - res.status(200).send(deactivated_user.to_safe_json()); + const user = await db.user.instance().find_one( { id:parseInt( id ) } ); + if (!user) return next( createError( 404, 'User not found' ) ); + const deactivated_user = await user.deactivate( deactivated_by_id ); + res.status( 200 ).send( deactivated_user ); } catch (error) { - logger.error(`Deactivate user error: ${error.message}`); - next(createError(error.status || 400, error.message)); + logger.error( `Deactivate user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); } }, @@ -153,16 +153,16 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async reactivate(req, res, next) { + async reactivate( req, res, next ) { try { const { id } = req.params; - const user = await db.user.instance().find_one({ id: parseInt(id) }); - if (!user) return next(createError(404, 'User not found')); + const user = await db.user.instance().find_one( { id:parseInt( id ) } ); + if (!user) return next( createError( 404, 'User not found' ) ); const reactivated_user = await user.reactivate(); - res.status(200).send(reactivated_user.to_safe_json()); + res.status( 200 ).send( reactivated_user ); } catch (error) { - logger.error(`Reactivate user error: ${error.message}`); - next(createError(error.status || 400, error.message)); + logger.error( `Reactivate user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); } }, @@ -173,17 +173,29 @@ module.exports = { * @param {Function} next - Express next middleware function * @returns {Promise} */ - async soft_delete(req, res, next) { + async soft_delete( req, res, next ) { try { const { id } = req.params; const { deleted_by_id } = req.body; - const user = await db.user.instance().find_one({ id: parseInt(id) }); - if (!user) return next(createError(404, 'User not found')); - const deleted_user = await user.soft_delete(deleted_by_id); - res.status(200).send(deleted_user.to_safe_json()); + const user = await db.user.instance().find_one( { id:parseInt( id ) } ); + if (!user) return next( createError( 404, 'User not found' ) ); + const deleted_user = await user.soft_delete( deleted_by_id ); + res.status( 200 ).send( deleted_user ); } catch (error) { - logger.error(`Soft delete user error: ${error.message}`); - next(createError(error.status || 400, error.message)); + logger.error( `Soft delete user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); + } + }, + + async get_current_user( req, res, next ) { + try { + const { id } = req.user; + const user = await db.user.instance().find_one( { id: id } ); + if (!user) return next( createError( 404, 'User not found' ) ); + res.status( 200 ).send( user ); + } catch (error) { + logger.error( `Get current user error: ${ error.message }` ); + next( createError( error.status || 400, error.message ) ); } } }; \ No newline at end of file diff --git a/src/middleware/passport.js b/src/middleware/passport.js index 2d37cff..3070197 100755 --- a/src/middleware/passport.js +++ b/src/middleware/passport.js @@ -1,7 +1,33 @@ -const JWTStrategy = require( 'passport-jwt' ).Strategy, - ExtractJwt = require( 'passport-jwt' ).ExtractJwt; +const JWTStrategy = require( 'passport-jwt' ).Strategy; const config = require( '../config/default.json' ); const db = require( '../models' ); +const jwt = require("jsonwebtoken"); + +const cookieExtractor = function(req) { + let token = null; + if (req?.cookies?.['phase_request_token']) { + const decoded = jwt.decode(req?.cookies?.['phase_request_token'].trim(), config.keys.secret); + token = decoded.request_token; + } + return token; +}; + +const authenticate = function ( JWTPayload, callback ) { + // return callback(null, {id: 1}); + logger.debug( `JWT-JWTPayload: ${ JSON.stringify( JWTPayload ) }` ); + return db.user.find_one( { email:JWTPayload.email, is_active:true, is_delete:false } ) + .then( async user => { + logger.debug( 'passport: ' + user ); + if (!user?.id) { + return callback( null, false ); + } else if (user?.id) { + user.roles = user.get_user_roles().then( roles => roles.map( role => role.name ) ); + return callback( null, user ); + } else { + return callback( null, false ); + } + } ).catch( error => callback( error, false ) ); +} // Hooks the JWT Strategy. function hookJWTStrategy( passport ) { @@ -10,25 +36,10 @@ function hookJWTStrategy( passport ) { // options.secretOrKey = process.env['HASH_KEY']; options.secretOrKey = config.keys.secret; - options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); + options.jwtFromRequest = cookieExtractor; options.ignoreExpiration = true; - passport.use( 'jwt', new JWTStrategy( options, function ( JWTPayload, callback ) { - // return callback(null, {id: 1}); - logger.debug( `JWT-JWTPayload: ${ JSON.stringify( JWTPayload ) }` ); - return db.user.find_one( { email:JWTPayload.email, is_active:true, is_delete:false } ) - .then( async user => { - logger.debug( 'passport: ' + user ); - if (!user?.id) { - return callback( null, false ); - } else if (user?.id) { - user.roles = user.get_user_roles().then( roles => roles.map( role => role.name ) ); - return callback( null, user ); - } else { - return callback( null, false ); - } - } ).catch( error => callback( error, false ) ); - } ) ); + passport.use( 'jwt', new JWTStrategy( options, authenticate ) ); } module.exports = hookJWTStrategy; \ No newline at end of file diff --git a/src/middleware/routeHelpers.js b/src/middleware/routeHelpers.js index 0963c46..94a8293 100755 --- a/src/middleware/routeHelpers.js +++ b/src/middleware/routeHelpers.js @@ -1,7 +1,8 @@ const validate_auth = function ( passport, context='jwt') { async function checkKey( req, res, next ) { const { authorization, apikey:apiKey } = req.headers; - if (authorization) return passport.authenticate( context, { session:false } )(req, res, next); + const { phase_request_token } = req.cookies; + if (authorization||phase_request_token) return passport.authenticate( context, { session:false } )(req, res, next); else if (!authorization && !apiKey) return passport.authenticate( context, { session: false } )(req, res, next); next(); } diff --git a/src/models/authentication.model.js b/src/models/authentication.model.js index c9cc191..66eb22f 100644 --- a/src/models/authentication.model.js +++ b/src/models/authentication.model.js @@ -77,6 +77,10 @@ class Authentication extends Model { `; this.default_order_by = 'ORDER BY auth.user_id ASC'; this.instance = _props => new Authentication( _props ); + this.toJSON = () => { + const { password, password_salt, password_verification_token, password_reset_token, ...safeData } = super.toJSON(); + return safeData; + }; }; /** @@ -177,11 +181,6 @@ class Authentication extends Model { locked_date:null } ); }; - - to_safe_json() { - const { password, password_salt, password_verification_token, password_reset_token, ...safeData } = this.toJSON(); - return safeData; - }; } module.exports = Authentication; \ No newline at end of file diff --git a/src/models/user.model.js b/src/models/user.model.js index 080e3bf..116af0f 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -54,12 +54,18 @@ class User extends Model { `; this.base_list_query = ` SELECT u.id, u.email, u.first_name, u.middle_name, u.last_name, u.initials, u.nickname, - u.created_by_id, u.created_at, u.is_active, u.deactivated_by_id, u.deactivated_at + u.created_by_id, u.created_at, u.is_active, u.deactivated_by_id, u.deactivated_at, a.password, a.password_salt, + a.is_locked, a.locked_date FROM phase.users u + inner join phase.authentication a on u.id = a.user_id `; this.default_order_by = 'ORDER BY u.email ASC'; this.instance = _props => new User(_props); - } + this.toJSON = () => { + const { password, password_salt, ...safe_data } = super.toJSON(); + return safe_data; + }; + }; static async create(user_data) { const { @@ -219,11 +225,6 @@ class User extends Model { is_active() { return this.is_active === true; } - - to_safe_json() { - const { password, ...safe_data } = this.toJSON(); - return safe_data; - } } module.exports = User; \ No newline at end of file diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 8bc2953..a5582aa 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -5,6 +5,7 @@ const users_controller = require('../controllers/user.controller'); module.exports = (passport) => { router.post('/create', validate_auth(passport), users_controller.create); + router.get( '/current', validate_auth(passport), users_controller.get_current_user); router.get('/email/:email', validate_auth(passport), users_controller.find_by_email); router.get('/:id', validate_auth(passport), users_controller.find_one); router.get('/', validate_auth(passport), users_controller.find_many);