Tìm hiểu về Passport.js - Các bước xác thực tài khoản

Passport.js một module linh hoạt nhất của Nodejs trong việc hỗ trợ xác thực tài khoản (authentication). Nó được thiết kế thành một module vì vậy có thể làm cho Passport dễ dàng tích hợp vào trong ứng dụng của bạn. Passport.js được thiết kế là một middleware cho bạn khả năng tùy biến cao với rất nhiều các kịch bản authentication: có thể sử dụng Twitter, Facebook, Google thậm chí là qua username, password trong database. Trong bài viết chúng ta sẽ đi tìm hiểu sâu về cơ chế hoạt động của Passport.js nhé.

Thiết lập passport cho ứng dụng

Để sử dụng được passport bạn cần đi qua 3 bước:

  1. Require passport module và sử dụng middleware của passport là passport.initialize() and passport.session() vào express. Chú ý là ứng dụng express của bạn cần sử dụng đến express-session.
var app = require('express')();
var session = require('express-session');
var bodyParser = require('body-parser');
var passport = require('passport');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.use(session({
  secret : "secret",
  saveUninitialized: true,
  resave: true
}))

app.use(passport.initialize());
app.use(passport.session());

app.listen(3000);
  1. Cấu hình passport và thiết lập 2 hàm serializeUser, deserializeUser
var LocalStrategy = require('passport-local').Strategy;
var passport = require('passport');
var bcrypt = require('bcrypt');

passport.serializeUser(function(user, done) {
    done(null, user.id);
});

passport.deserializeUser(function(id, done) {
    db.user.findById(id).then(function (user) {
        done(null, user);
    }).catch(function (err) {
        console.log(err);
    })
});

passport.use(new LocalStrategy(
    function (username,password,done) {
        db.user.find({where : {
            username : username
        }}).then(function (user) {
            bcrypt.compare(password, user.password, function (err,result) {
                if (err) { return done(err); }
                if(!result) {
                    return done(null, false, { message: 'Incorrect username and password' });
                }
                return done(null, user);
            })
        }).catch(function (err) {
            return done(err);
        })
    }
));
  1. Thiết lập route dùng middleware passport.authenticate.
app.get('/login', function(req, res) {
  res.render('login');
});

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

Các bước xác thực tài khoản trong Passport.js

Ví dụ route xác thực tài khoản là /login. Khi người dùng đăng nhập vào vệ thống passport sẽ xử lý như sau:

  1. Khi người dùng submit form đăng nhập, một request POST được tạo ra tới route /login , nó sẽ chạy cái middleware passport.authenticate cho bạn.
  2. Như trên ta thiết lập kịch bản Local cho thằng passport.authenticate nên nó sẽ gọi đến cái kịch bản ta đã cài đặt.
  3. Nó lấy dữ liệu req.body.username và req.body.passport rồi gán cho hàm verify local.
  4. Ở đây như cấu hình ở trên ta thấy chúng ta sẽ query database rồi kiểm tra xem passport của người dùng đưa lên có đúng không.
  5. Trong trường hợp Error từ db ta gọi đến callback là done với param là err( done(err)) Khi mà nó không tìm thấy được người dùng hợp lý ta gọi đến thằng done(null,false). Còn nếu thông tin đăng nhập đúng ta gọi done(null,user).
  6. Khi callback done được gọi, nó sẽ lấy dữ liệu err, user và dữ liệu bạn custom thêm nếu có trả lại cho thằng passport.authenticate.
  7. Nếu dữ liệu trả về của callback là null, true, xác thực thành công passport tiếp tục gọi hàm req.login( cái này tự gắn vào từng request khi bạn cài đặt passport ở bước 1)
  8. Hàm req.login gọi đến thằng passport.serializeUser mà ta đã định nghĩa trước đó. Hàm này truy cập vào đối tượng user mà ta trả về cho middleware passport.authenticate và xác định xem thành phần nào của đối tượng sẽ lưu vào trong session. Kết quả của hàm này là ta sẽ có đối tượng req.session.passport.user = các thông tin ta truyền vào trong serializeUser.Trong ví dụ bên trên thì nó là user.id
  9. Đồng thời với trên passport cũng có gắn thông tin user vào req.user.
  10. Việc xác thực kết thục, hàm requestHandler sẽ được gọi đưa chúng tra đến trang đã thiết lập.

Xác thực các request sau khi đăng nhập.

Trong các request tiếp theo hệ thống passport hoạt động như sau:

  1. Với mỗi request , express sẽ load các sữ liệu trong session ra và gắn nó và đối tượng request (req.session). Ở trên ta đã sử dụng hàm serializeUser để đưa dữ liệu vào session nên ta có thể tìm thấy dữ liệu đó tạo req.session.passport.user.
  2. Middleware khởi động passport (passport.initialize) sẽ check xem trong request session có passport.user không. Nếu chưa có là chưa xác thực thì thằng req.session.passport.user = {}
  3. Tiếp đó passport.session được gọi. Nếu thấy passport.user trong sesion request đó được tính là đã xác thực.
  4. Khi request được tính là đã xác thực nó sẽ gọi hàm passport.deserializeUser. Hàm này sử dụng thông tin trong session để lấy dữ liệu đầy đủ về thằng user rồi gắn nó vào req.user.

Tóm tắt lại các hàm và middleware của passport

  • passport.initialize : middleware được gọi ở từng request, kiểm tra session lấy ra passport.user nếu chưa có thì tạo rỗng.
  • passport.session: middleware sử dụng kịch bản Passport , sử dụng session lấy thông tin user rồi gắn vào req.user.
  • passport.deserializeUser : hàm được gọi bởi passport.session .Giúp ta lấy dữ liệu user dựa vào thông tin lưu trên session và gắn vào req.user
  • passport.authenticate: middleware giúp ta gắn kịch bản local vào route.
  • passport.serializeUser: hàm được gọi khi xác thực thành công để lưu thông tin user vào session

Các hàm hỗ trợ thêm cho từng request

Với từng request passport gắn thêm cho bạn 4 hàm :

  • req.login()
  • req.logout()
  • req.isAuthenticated()
  • req.isUnauthenticated()

Tham khảo: