A better way to check for authorization of API requests using Express JS

夙愿已清 提交于 2019-12-10 15:36:34

问题


I have a super redundant server.js file, since nearly every method in it belonging to the REST API starts like that, as I have to check at the API requests whether the client is authorized to ask for the particular thing.

var jwt = require('jsonwebtoken');

// ...

app.get('/getsomething', function(req, res) {
  "use strict";
  var token = req.headers[tokenName];
  if (token) {
    jwt.verify(token, app.get('some_secret'), {
      ignoreExpiration: false
    }, function(err, decoded) {
      if (err || typeof decoded === "undefined") {
        res.json({
          status: 401,
          message: "unauthorized"
        });
      }
      else { // actual code starts here...

What would be a better way?


回答1:


You should have a middleware that checks every request coming in, something like this:

// AUTHENTICATION
app.use(async (req) => {
    try {
        const token = req.headers.authorization
        const { person } = await jwt.verify(token, SECRET)
        req.person = person
        return req.next()
    } catch (e) {
        return req.next()
    }
})

In this example, both success or fail allows the user to pass through to the route, but req.person is only populated if the user is logged in. Keep in mind, this is an asynchronous function due to async keyword. It returns a promise and we are using try/catch architecture. This allows us to use await which halts execution until jwt.verify() has populated person. This then makes req.person available in every route. In this example, person is a property that you specified when you did jwt.sign() to create the JWT. Here is an example to jog your memory:

jwt.sign({
  person: 'some unique identifier'
}, 'secret', { expiresIn: '1y' })

In this way, req.person can only ever be populated by decoding a valid JWT. Do not be confused by const { person } = await jwt.verify(token, SECRET). This is the same as writing:

const decodedJWT = await jwt.verify(token, SECRET)
const person = decodedJWT.person

In every protected route, you can simply make the first line of code something like:

// PROTECTED
app.get('/radical', async (req, res, next) => {
    try {
        // If req.person is falsy, the user is not logged in
        if (!req.person) return res.status(403).render('error/403')
        // Otherwise, the user is logged in, allow him/her to continue
        // Replace res.render() with res.json() in your case.
        return res.render('radical/template', {
            person: req.person
        })
    } catch (e) {
        return next(e)
    }
})

This example demonstrates EJS View Templating Engine:

  • See: https://www.npmjs.com/package/ejs

Then, after all your routes, put a splat route, and an error handling middleware:

// SPLAT ROUTE
app.get('*', (req, res, next) => {
    return res.status(404).render('error/404')
})

// ERRORS
app.use((err, req, res, next) => {
    res.status(500).render('error/500')
    throw err
})

The error handling middleware needs to come last, and it has an additional 4th parameter which is err and it contains the value of the parameter of next() only if you pass a parameter to it, ie: next('Error happened').

This above code works without any changes for GraphQL as well. To handle authentication in GraphQL, simply examine this:

// GRAPHQL
app.use('/graphql', bodyParser.json(), graphqlExpress((req) => {
    const context = {
        person: req.person
    }
    return {
        schema,
        context,
        rootValue: null,
        formatError: (error) => ({
            message: error.message,
            locations: error.locations,
            stack: error.stack,
            path: error.path
        }),
        debug: true
    }
}))

The GraphQL endpoint must be mounted after the authentication middleware. The logged in user will be available in every resolver as context.person, or if the request is illegal, context.person will be falsy. I mention this for any others searching in the future.



来源:https://stackoverflow.com/questions/46615798/a-better-way-to-check-for-authorization-of-api-requests-using-express-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!