Using Express middleware for authentication

in #nodejs7 years ago (edited)

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})
})
Sort:  

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


Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Congratulations @thebluefish! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

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:

The Steem community has lost an epic member! Farewell @woflhart!
SteemitBoard - Witness Update
Do not miss the coming Rocky Mountain Steem Meetup and get a new community badge!
Vote for @Steemitboard as a witness to get one more award and increased upvotes!