Using Express middleware for authentication
For the longest time, my bot platform was secured by a simple hard-coded password check on the client-side. This was OK for a placeholder, but isn't security by any means. In the recent rewrite, I implemented a solution using JSON Web Tokens within Express.
In the new version of my platform, we assume there to only be one user. We also assume that this user will host an instance of the platform themselves, so security is strictly "Am I logged in or not" - We do not need to worry about users attempting to access resources belonging to other users.
We allow multiple modules bound to a context object. These modules are all exported as:
export {default as account} from './account' // Provides general account functionality
export {default as alerts} from './alerts' // Pushes alerts to webapp and mobile app
export {default as auth} from './auth' // Provides authentication endpoints
export {default as exchange} from './exchange' // Provides exchange data feed
export {default as repl} from './repl' // REPL scripting server-side
export {default as scripts} from './scripts' // User scripts running server-side
export {default as wallet} from './wallet' // Provides wallet management
In our primary Express app, we can import these modules:
import * as base_modules from './modules'
import auth from './modules/auth/api.js'
And then gracefully mount them:
// Mount public modules
app.use('/auth', auth);
// Ensure valid credentials for rest of API
app.use(function(req, res, next) {
let jwtobj = req[jwt.options.reqProperty];
// Validate session
if(jwtobj && jwtobj.payload['session'] && config.get('sessions').indexOf(jwtobj.payload['session']).value() >= 0) {
return next();
}
// It's important that we fail closed
console.log(`401 - not authenticated - ${req.originalUrl}`);
return res.status(401).json({error: 'Not Authenticated'});
});
// Mount private modules
_.each(base_modules, (v, i) => {
if(typeof v.constructor.api == 'function') {
console.log(`mounting api endpoint, ${i}`)
app.use(`/${i}`, v.constructor.api)
}
});
This allows us to avoid having to authenticate every API endpoint. Authentication here will fail before any 'private' APIs can be hit, allowing us to write clean and verbose code.
Example:
// Scripts route
router.all('/', async (req, res) => {
// Hooray, we already know we have authenticated. We do not need to do any additional checking before running the below dangerous code
let result = await context.scripts.get_scripts()
res.json({success: true, result: result})
})
Congratulations @thebluefish! You have received a personal award!
1 Year on Steemit
Click on the badge to view your Board of Honor.
Do not miss the last post from @steemitboard!
Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes
Congratulations @thebluefish! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!