mikenicholson / passport-jwt

Passport authentication using JSON Web Tokens
MIT License
1.97k stars 214 forks source link

How to get user from JWT without 401 on fail #145

Closed good-idea closed 6 years ago

good-idea commented 6 years ago

I'm authenticating calls to my express API using passport. I have a pretty standard setup:

/* Passport Setup */

const jwtOptions = {
    jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
    secretOrKey: config.auth.passport.key,
}

passport.use(
    'jwt',
    new JWT.Strategy(jwtOptions, (payload, done) => {
        console.log('Using JWT Strategy')
        User.findOne({ email: payload.email }, (err, user) => {
            if (err) {
                return done(err, false)
            }
            if (user) {
                done(null, user)
            } else {
                done(null, false)
            }
        })
    }),
)

/* Middleware */

const checkToken = passport.authenticate('jwt', { session: false })

const logAuthInfo = (req, res, next) => {
    console.log(req.headers)
    console.log(req.user)

}

/* Routes */

app.use(passport.initialize())

app.use('/graphql', checkToken, logAuthInfo, graphqlHTTP(graphQLConfig))
// other REST routes, including login

When a request is made to /graphql with this token, everything works as expected. But, an unauthenticated request (with no token) returns a 401. What I'd like to do differently is use the checkToken middleware on all requests, assigning req.user to either the authenticated user data or false. I'd then handle any authorization elsewhere.

I'd like to be able to do something like this:

const getUserFromToken = (req, res, next) => {
    const jwt = ExtractJwt.fromAuthHeaderWithScheme('Bearer')(req)
    const user = deserializeJWT(jwt) // How do I do this?
        if (user) req.user = user
    next()
}

How can I achieve this?

good-idea commented 6 years ago

Ok, i figured this out shortly after posting this. For anyone coming here with the same question -- the solution is not using passport-jwt to achieve this, but rather the underlying jsonwebtoken.

My working middleware now looks like:

const jwt = require('jsonwebtoken')
const PassportJwt = require('passport-jwt')

const getUserFromToken = (req, res, next) => {
    const token = PassportJwt.ExtractJwt.fromAuthHeaderWithScheme('Bearer')(req)
    jwt.verify(token, jwtSecret, (err, decoded) => {
        if (err) {
            req.user = false
            next()
            return
        }
        req.user = decoded
        next()
    })
}

app.use(getUserFromToken)
app.use('/graphql', graphqlHTTP(graphQLConfig))

// Elsewhere, in my GraphQL resolvers

const userQuery = (obj, args, request, info) => {
        //                    ^^^^^^^
        // I've also seen this parameter referred to as 'context'
    console.log(request.user) // either 'false' or the serialized user data

        if (req.user) {
            // do things that this user is allowed to do...
        } else {
           // user is not logged in, do some limited things..
       } 
}