I keep getting "Login sessions require session support" when I try to use tokens

Viewed 73

So I am following a tutorial on how to use JSON tokens and I am getting an error, it was working fine using sessions but I can't figure out why I am having trouble, it is the exact code

this is my authenticate.js file:

const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const User = require("./models/user");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const jwt = require("jsonwebtoken"); // used to create, sign, and verify tokens

const config = require("./config.js");

exports.local = passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

exports.getToken = function (user) {
    return jwt.sign(user, config.secretKey, { expiresIn: 3600 });
}; // config.secretKey is a string of random numbers

const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;

exports.jwtPassport = passport.use(
    new JwtStrategy(opts, (jwt_payload, done) => {
        console.log("JWT payload:", jwt_payload);
        User.findOne({ _id: jwt_payload._id }, (err, user) => {
            if (err) {
                return done(err, false);
            } else if (user) {
                return done(null, user);
            } else {
                return done(null, false);
            }
        });
    })
);

exports.verifyUser = passport.authenticate("jwt", { session: false });

This is my app.js file (the main file):

const createError = require("http-errors");
const express = require("express");
const path = require("path");
const logger = require("morgan");
const config = require("./config");

const indexRouter = require("./routes/index");
const usersRouter = require("./routes/users");

const mongoose = require("mongoose");

const passport = require("passport");

const url = config.mongoUrl;
const connect = mongoose.connect(url, {
    useCreateIndex: true,
    useFindAndModify: false,
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

connect.then(
    () => console.log("Connected correctly to server"),
    (err) => console.log(err)
);

const app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// app.use(cookieParser("12345-67890-09876-54321"));

app.use(passport.initialize());

app.use("/", indexRouter);
app.use("/users", usersRouter);

app.use(express.static(path.join(__dirname, "public")));


// catch 404 and forward to error handler
app.use(function (req, res, next) {
    next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get("env") === "development" ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render("error");
});

module.exports = app;

this is the routes/users.js file (I believe the problem is here because I can sign-up (create new users) but I can't login with the same users)

const express = require("express");
const User = require("../models/user");
const passport = require("passport");
const authenticate = require("../authenticate");

const router = express.Router();

/* GET users listing. */
router.get(
    "/",
    function (req, res, next) {
        res.send('send users')
    }
);

router.post("/signup", (req, res) => {
    User.register(
        new User({ username: req.body.username }),
        req.body.password,
        (err, user) => {
            if (err) {
                res.statusCode = 500;
                res.setHeader("Content-Type", "application/json");
                res.json({ err: err });
            } else {
                if (req.body.firstname) {
                    user.firstname = req.body.firstname;
                }
                if (req.body.lastname) {
                    user.lastname = req.body.lastname;
                }
                user.save((err) => {
                    if (err) {
                        res.statusCode = 500;
                        res.setHeader("Content-Type", "application/json");
                        res.json({ err: err });
                        return;
                    }
                    passport.authenticate("local")(req, res, () => {
                        res.statusCode = 200;
                        res.setHeader("Content-Type", "application/json");
                        res.json({
                            success: true,
                            status: "Registration Successful!",
                        });
                    });
                });
            }
        }
    );
});

// I tried to add a console.log inside of the .post() route but it never reach it
router.post(
    "/login",
    passport.authenticate("local"),
    (req, res) => {
        const token = authenticate.getToken({ _id: req.user._id });
        res.statusCode = 200;
        res.setHeader("Content-Type", "application/json");
        res.json({
            success: true,
            token: token,
            status: "You are successfully logged in!",
        });
    }
);

router.get("/logout", (req, res, next) => {
    if (req.session) {
        req.session.destroy();
        res.clearCookie("session-id");
        res.redirect("/");
    } else {
        const err = new Error("You are not logged in!");
        err.status = 401;
        return next(err);
    }
});

module.exports = router;

Basically, every time that i go to localhost:3000/users/login and send a POST request with the username and password, it tells me that I need to use express-session but I am trying to use tokens instead of session

1 Answers

The problem is caused when passport.authenticate('local') is called in routes/users.js file. It is a middleware that automatically calls req.login function in case correct username and password is provided. The req.login() in turn, implements sessions in order to serialise the user in the session.

You can solve the issue by adding another parameter to passport.authenticate() as passport.authenticate('local', {session: false}. This ensures sessions are not implemented in the 'local' strategy and subsequently login can be performed.

Related