mikenicholson / passport-jwt

Passport authentication using JSON Web Tokens
MIT License
1.96k stars 213 forks source link

How to handle a route that authentication is optional? #110

Closed zackiles closed 7 years ago

zackiles commented 7 years ago

Coming from express cookie session, if a session is found it will automatically set req.user with a logged in user. I'd like to create routes that authenticates req.user for authenticated users but still allow non authenticated users to access the route. Currently the passport.authenticate('jwt') function will just throw an error if there is no authentication forcing me to create authenticated-only routes. Would anybody be as kind to give me a workaround? Maybe a way to call authenticate not within a route handler?

juhaelee commented 7 years ago

@zackiles did you find a solution for this?

jeremieca commented 7 years ago

Same problem for me !

HRK44 commented 6 years ago

Any solution to look at for this?

camovb commented 6 years ago

This might be a bit late, but it's actually pretty easy to do.

I'm using the fromAuthHeaderAsBearerToken() as extractor

Configuration

const jwtOptions: JWTStrategyOptions = {
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey: Config.auth.jwtSecret()
}

Auth Service

class AuthService {

    // Register the strategy
    constructor() {
        passport.use('authenticate', this.jwtAuthenticateWithoutUserStrategy())
    }

    // ...
    // Setup the strategy
    private jwtAuthenticateWithoutUserStrategy() {
        return new JWTStrategy(jwtOptions, (payload, callback) => {
            if (payload.id) {
                callback(null, { id: payload.id })
            } else {
                const error = { message: 'Invalid user' }
                callback(error, null, error)
            }
        })
    }

    // ...
    // authentication method `AuthService.authenticate`
    public get authenticate() {
        return passport.authenticate('authenticate', {session: false})
    }

    // ...
    // Optional authentication
    public optionalAuthenticate = (request: Request, response: Response, next: NextFunction) => {
            const auth = request.header('Authorization')
             if (auth) {
                this.authenticate(request, response, next)
            } else {
                next()
        }
    }
}

How to use

const router = Router()
router.get('/myPersonalizedContent', AuthService.optionalAuthenticate, (req, res, next) => {
    if (req.user && req.user.id) {
        //TODO: Do something with the user ID and fetch some special data
    } else {
        //TODO: The user is not logged in
    }
})

So the AuthService.optionalAuthenticate checks first if the token is set in the header, if you are using some other extraction method you would need to update this. If the token is set lets the Strategy handle the token and validate everything for us. If the Authorization is not set... we don't really care and just delegate all the handling to the next function.

demisx commented 4 years ago

This is what the passport-anonymous strategy is for. Just chain it after JWT one.

SuspiciousLookingOwl commented 4 years ago

The quick guide on using passport-anonymous with passport-jwt

  1. npm install passport-anonymous
  2. use the strategy
    passport.use(new AnonymousStrategy());
  3. Add to middleware
    router.get("/", passport.authenticate(["jwt", "anonymous"], {session:false}), (req, res) => {
    if (req.user) {
        console.log("User logged in", req.user);
    } else {
        console.log("User isn't logged in");
    }
    });

    Keep in mind that jwt should come first, then anonymous

Brenndoerfer commented 3 years ago

passport-anonymous is indeed the right solution.

You can define functions add as middleware on certain routes:

module.exports.requiresAuth = passport.authenticate('jwt', {session: false, failureRedirect: '/'});
module.exports.canHaveAuth = passport.authenticate(['jwt', 'anonymous'], {session: false}