Code Guru
Code Guru

Reputation: 15598

how to use passport.js in a proper way

I am trying passport library to authenticate api request. To start I have created a NodeJS application with the express framework. The project contains some apis that serve some data. In public folder it contains index.html page having username and password field.

Index.html

<form action="/login" method="post">
    <div>
        <label>Username:</label>
        <input type="text" name="name"/>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password"/>
    </div>
    <div>
        <input type="submit" value="Log In"/>
    </div>
</form>

Created a server.ts that create a http server and listen on some port and created apis using express framework.

Server.ts

let userList: User[] = [new User(1, "Sunil"), new User(2, "Sukhi")];

let app = express();

// passport library
let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;

// middlewares
app.use(express.static("public"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ resave: false, saveUninitialized: true, secret: "secretKey123!!" }));

// passport middleware invoked on every request to ensure session contains passport.user object
app.use(passport.initialize());
// load seriliazed session user object to req.user 
app.use(passport.session());

// Only during the authentication to specify what user information should be stored in the session.
passport.serializeUser(function (user, done) {
    console.log("Serializer : ", user)
    done(null, user.userId);
});

// Invoked on every request by passport.session
passport.deserializeUser(function (userId, done) {
        let user = userList.filter(user => userId === user.userId);
        console.log("D-serializer : ", user);
        // only pass if user exist in the session
        if (user.length) {
            done(null, user[0]);
        }
});
// passport strategy : Only invoked on the route which uses the passport.authenticate middleware.
passport.use(new LocalStrategy({
    usernameField: 'name',
    passwordField: 'password'
},
    function (username, password, done) {
        console.log("Strategy : Authenticating if user is valid  :", username)
        let user = userList.filter(user => username === user.userName)
        if (!user) {
            return done(null, false, { message: 'Incorrect username.' });
        }
        return done(null, user);
    }
));

app.post('/login', passport.authenticate('local', {
    successRedirect: '/done',
    failureRedirect: '/login'
}));

app.get('/done', function (req, res) {
    console.log("Done")
    res.send("done")
})

app.get('/login', function (req, res) {
    console.log("login")
    res.send("login")
})

// http server creation
let server = http.createServer(app);

server.listen(7000, () => {
    console.log('Up and running on port 7000');
});

Now when I hit localhost:7000 it opens the login page and when I click submit with username from userList it returns the done otherwise login. This is fine.

Now every call goes through deserializeUser method.

The problem is when I call other URLs directly without hitting /login (authenticates the user) they also work fine and return data.

I was expecting that if the request is not authenticated all other calls will fail as deserializeUser is intercepting every request but in this case, no passport method is called.

Is this how it works? or I am missing something?

Upvotes: 1

Views: 1057

Answers (2)

Code Guru
Code Guru

Reputation: 15598

I was missing middleware to authenticate all subsequent requests. So I have created isAuthenticated method (thanks @Sombrero).

// request interceptor that will check user authentication
private static isAuthenticated = (req, res, next) => {
    console.log("Authenticating :", req.originalUrl)
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login');
};

and then in every request

app.get('/done', isAuthenticated, (req, res) => {
    res.send("done")
});

but this was tough to use isAuthenticated method in every request. So I created an array of API list that is public and added middleware to intercept every request and updated isAuthenticated method to ignore public apis

// list of apis for which authentication is not required
private static publicApiList: string[] = ["/login"];

// request interceptor that will check user authentication
private static isAuthenticated = (req, res, next) => {
    console.log("Authenticating :", req.originalUrl)
    if (req.isAuthenticated() || Server.publicApiList.indexOf(req.originalUrl) > -1) {
        return next();
    }
    res.redirect('/login');
};

and then used this method as middleware

app.use(Server.isAuthenticated)

Upvotes: 0

Sombrero
Sombrero

Reputation: 376

You need to add a middleware, for check if your user is authenticated:

isAuthenticated = (req, res, next) => {
    if (req.isAuthenticated()) {
      //if user is logged in, req.isAuthenticated() will return true 
      return next();
    }
    res.redirect('/login');
};

And you have to use that middleware like that:

//if user not authenticated, he will be redirect on /login
app.get('/done', isAuthenticated, (req, res) => {
    res.send("done")
});

Upvotes: 2

Related Questions