Xác thực đăng nhập bằng Passport – Bắt đầu với Local Authentication

Bài viết này là một phần của series bài viết Xác thực đăng nhập bằng Passport:

  1. Bắt đầu với Local Authentication
  2. Facebook
  3. Twitter
  4. Google
  5. Liên kết các tài khoản

Trên thực tế tất cả các ứng dụng của chúng ta đều có chức năng xác thực và đăng nhập. Việc xác thực và đăng nhập trong Nodejs là khá phức tạp. Trong series bài viết này chúng ta sẽ xây dựng chức năng đăng nhập và xác thực cho ứng dụng Nodejs bằng package Passport.

Chúng ta sẽ làm những gì?

Chúng ta sẽ xây dựng một ứng dụng có thể:

  • Đăng nhập bằng tài khoản local và đăng ký (passport-local)
  • Đăng nhập bằng tài khoản Facebook và tạo tài khoản mới (passport-facebook)
  • Đăng nhập bằng tài khoản Twitter và tạo tài khoản mới (passport-twitter)
  • Đăng nhập bằng tài khoản Google+ và tạo tài khoản mới (sử dụng oauth với passport-google-oauth)
  • Thiết lập các trang chỉ có thể xem được sau khi đã xác thực.
  • Mã hóa mật khẩu khi lưu vào database cho local account (bcrypt-nodejs)
  • Hiển thị thông báo lỗi ra phía người dùng (connect-flash, yêu cầu sử dụng express version > 3.0)
  • Liên kết tất cả các tài khoản mạng xã hội dưới một tài khoản.
  • Cho phép một người dùng có thể hủy liên kết một tài khoản mạng xã hội.

Đây là những gì chúng ta sẽ xây dựng:

Và đây là những gì sau khi người dùng đã đăng nhập, liên kết tất cả các tài khoản mạng xã hội:

Trong bài viết này, chúng ta sẽ chỉ tập trung vào việc đăng nhập và đăng ký cho tài khoản local. Đây là phần đầu tiên của series chúng ta sẽ cài đặt ứng dụng của mình, vì vậy đây sẽ là một bài viết khá dài.

Thiết lập ứng dụng

Để thiết lập một ứng dụng Node cơ bản, chúng ta sẽ cần vài thứ: các npm packages, file config, file models, và các routes.

Cấu trúc ứng dụng

- app
------ models
---------- user.js  <!-- user model -->
------ routes.js    <!-- Toàn bộ routes cho ứng dụng -->
- config
------ auth.js      <!-- File sẽ lưu trữ các thông tin để yêu cầu xác thực từ fb, tw, hay google. Bao gồm clientId, secretKey -->
------ database.js  <!-- File chứa thông tin kết nối tới database, chúng ta sẽ dùng Mongodb -->
------ passport.js  <!-- Cấu hình các cài đặt và xử lý cho Passport -->
- views
------ index.ejs    <!-- Hiển thị trang chủ với các nút đăng nhập và đăng ký -->
------ login.ejs    <!-- Hiển thị trang đăng nhập -->
------ signup.ejs   <!-- Hiển thị trang đăng ký -->
------ profile.ejs  <!-- Sau khi user đăng nhập thành công, họ sẽ thấy thông tin của họ ở trang này -->
- package.json      <!-- Cấu hình ứng dụng và các packages chúng ta sẽ dùng -->
- server.js         <!-- Cài đặt ứng dụng và sẽ là file thực thi -->

Tạm thời chúng ta sẽ tạo ra tất cả các file và thư mục như trên, chúng ta sẽ sử dụng tất cả các file khi đọc tới bài cuối cùng của series này.

Ghi chú: Để bắt đầu với một ứng dụng Node chúng ta có thể dùng lệnh npm init -y tại thư mục dự án. Lệnh này sẽ tạo ra file package.json lưu trữ các thông tin cơ bản của dự án.

Packages package.json

Các bạn có thể sử dụng sẵn nội dung bên dưới rồi dùng lệnh npm install để kéo các gói cần thiết về local. Hoặc gõ lệnh

npm install -S express ejs mongoose passport passport-local passport-facebook passport-twitter passport-google-oauth connect-flash bcrypt-nodejs morgan body-parser cookie-parser method-override express-session

(-S để lưu thông tin các gói vào file package.json)

{
  "name": "node-authentication-passport",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "ctw",
  "license": "ISC",
  "dependencies": {
    "bcrypt-nodejs": "0.0.3",
    "body-parser": "^1.18.1",
    "connect-flash": "^0.1.1",
    "cookie-parser": "^1.4.3",
    "ejs": "^2.5.7",
    "express": "^4.15.4",
    "express-session": "^1.15.5",
    "method-override": "^2.3.9",
    "mongoose": "^4.11.11",
    "morgan": "^1.8.2",
    "passport": "^0.4.0",
    "passport-facebook": "^2.1.1",
    "passport-google-oauth": "^1.0.0",
    "passport-local": "^1.0.0",
    "passport-twitter": "^1.0.4"
  }
}

Hầu hết các thư viện được sử dụng đều phổ biến:

  • Express là framework chính.
  • Ejs là template engine.
  • Mongoosse thư viện tạo modeling cho cơ sở dữ liệu MongoDB.
  • Passport giúp xác thực người dùng với nhiều phương thức khác nhau.
  • Bcrypt-nodejs mã hóa password khi lưu vào db.

Sau khi đã cài đặt đầy đủ các thư viện, chúng ta sẽ bắt đầu xây dựng ứng dụng từ file server.js

Thiết đặt cơ bản cho ứng dụng server.js

// server.js

// set up ======================================================================
// Lấy tất cả các công cụ, thư viện chúng ta cần
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');

var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');

var configDB = require('./config/database.js');

// configuration ===============================================================
mongoose.connect(configDB.url); // kết nối tới db

// require('./config/passport')(passport); // pass passport for configuration

// cài đặt ứng dùng express
app.use(morgan('dev')); // log tất cả request ra console log
app.use(cookieParser()); // đọc cookie (cần cho xác thực)
app.use(bodyParser()); // lấy thông tin từ html forms

app.set('view engine', 'ejs'); // cài đặt ejs là templating

// các cài đặt cần thiết cho passport
app.use(session({secret: 'ilovescodetheworld'})); // chuối bí mật đã mã hóa coookie
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session

// routes ======================================================================
require('./app/routes.js')(app, passport); // Load routes truyền vào app và passport đã config ở trên

// launch ======================================================================
app.listen(port);
console.log('The magic happens on port ' + port);

Chúng ta comment đoạn cấu hình cho passport. Chúng ta sẽ bỏ comment nó khi hoàn thành nội dung cho file config/passport.js

Chú ý: Thứ tự cấu hình đối tượng passport rất quan trọng. Chúng ta khởi tạo đối tượng passport ở trên cùng bằng câu lệnh var passport = require('passport');. Sau đó chúng ta truyền đối tượng này vào logic trong file config/passport.js. Cuối cùng chúng ta đẩy đối tượng này vào app/routes.js, trong file này nó sẽ sử dụng đối tượng passport đã được cấu hình.

Với file này chúng ta sẽ có một server chạy trên cồng 8080 (nếu chúng ta không truyền biến môi trường PORT).

npm startSau đó truy cập bằng trình duyệt vào địa chỉ http://localhost:8080 chúng ta sẽ thấy ứng dụng của chúng ta. (Tất nhiên giờ thì nó chưa có gì)

Tự động khởi động lại ứng dụng: Mặc định ứng dụng node sẽ không tự khởi động lại để cập nhật khi code có sự thay đổi, như vậy sẽ mất nhiều thời gian cho việc phát triển. Chúng ta có thể sử dụng một package là nodemon. Để cài đặt npm install nodemon -gvà sử dụng bằng lệnh nodemon server.js

Giờ chúng ta sẽ hoàn thiện những phần còn lại của ứng dụng databaseroutesuser model, và passport configuration. Trước tiên là database configuration và routes.

Database Config config/database.js

Chúng ta đã gọi module này trong file server.js. Giờ chúng ta phải cài đặt cho nó:

// config/database.js
module.exports = {
    'url': process.env.MONGODB_URL || "mongodb://localhost:27017/node-auth"
    // ví dụ: mongodb://<user>:<pass>@mongo.onmodulus.net:27017/databasename
};

Bạn có thể cài đặt Mongodb trên local hoặc sử dụng một dịch vụ cloud nào đó. Ở đây mình đã cài mongodb trên local(bằng docker).

Routes app/routes.js

Chúng ta sẽ thiết lập các route đơn giản. Chúng ta sẽ có những route sau:

  • Trang chủ (/)
  • Trang đăng nhập (/login)
  • Trang đăng ký (/signup)
  • Xử lý cho request POST /login
  • Xử lý cho request POST /signup
  • Trang thông tin tài khoản, sau khi đã đăng nhập (/profile)
// app/routes.js
module.exports = function (app, passport) {

    // =====================================
    // HOME PAGE (Với những link đăng nhập) ========
    // =====================================
    app.get('/', function (req, res) {
        res.render('index.ejs'); // load the index.ejs file
    });

    // =====================================
    // LOGIN ===============================
    // =====================================
    // Hiển thị form login
    app.get('/login', function (req, res) {
        // Hiển thị trang và truyển lại những tin nhắn từ phía server nếu có
        res.render('login.ejs', {message: req.flash('loginMessage')});
    });

    // Xử lý thông tin khi có người thực hiện đăng nhập
    // app.post('/login', chúng ta sẽ xử lý với passport ở đây);

    // =====================================
    // SIGNUP ==============================
    // =====================================
    // Hiển thị trang đăng ký
    app.get('/signup', function (req, res) {
        res.render('signup.ejs', {message: req.flash('signupMessage')});
    });

    // Xử lý thông tin khi có người đăng ký
    // app.post('/signup', chúng ta sẽ xử lý với passport ở đây);

    // =====================================
    // PROFILE SECTION =====================
    // =====================================
    // Đây là trang sẽ được bảo vệ, chỉ những người đã đăng nhập mới có thể xem được
    // Chúng ta sẽ sử dụng route middleware để kiểm tra xem người đó đã đăng nhập chưa
    // hàm isLoggedIn sẽ làm việc đó.
    app.get('/profile', isLoggedIn, function (req, res) {
        res.render('profile.ejs', {
            user: req.user // Lấy thông tin user trong session và truyền nó qua template
        });
    });

    // =====================================
    // LOGOUT ==============================
    // =====================================
    app.get('/logout', function (req, res) {
        req.logout();
        res.redirect('/');
    });
};

// route middleware để kiểm tra một user đã đăng nhập hay chưa?
function isLoggedIn(req, res, next) {
    // Nếu một user đã xác thực, cho đi tiếp
    if (req.isAuthenticated())
        return next();
    // Nếu chưa, đưa về trang chủ
    res.redirect('/');
}

app.post: Hiện tại chúng ta sẽ comment nó lại, đây là phần xử lý khi phía client POST thông tin lên server. Chúng ta comment nó vì chúng ta chưa cấu hình cho passport.

req.flash: Chúng ta sẽ tạo ra một loginMessage ở trong phần cấu hình passport.

req.flash: This is the connect-flash way of getting flashdata in the session. We will create the loginMessage inside our passport configuration.

isLoggedIn: Chúng ta phải bảo vệ trang profile, chỉ những user đã xác thực mới có thể vào được trang này. Middleware này sẽ đẩy một user về trang chủ khi user này cố truy cập trang http://localhost:8080/profile mà chưa đăng nhập.

Chúng ta chạy ứng dụng nhưng vẫn chưa thấy gì, bởi vì chúng ta chưa viết gì cho phần views. Giờ chúng ta sẽ hoàn thiện chúng. (Chúng ta đã làm gần hết các bước rồi, cố lên!)

Views views/index.ejs, views/login.ejs, views/signup.ejs

Ở đây chúng ta định nghĩa giao diện cho trang chủ, trang đăng nhập và trang đăng ký.

Home Page views/index.ejs

Trang chủ của chúng ta có nhiệm vụ hiển thị các links để đăng nhập.

<!-- views/index.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <!-- load fontawesome -->
    <style>
        body {
            padding-top: 80px;
        }
    </style>
</head>
<body>
<div class="container">

    <div class="jumbotron text-center">
        <h1><span class="fa fa-lock"></span> Node Authentication</h1>

        <p>Login or Register with:</p>

        <a href="/login" class="btn btn-default"><span class="fa fa-user"></span> Local Login</a>
        <a href="/signup" class="btn btn-default"><span class="fa fa-user"></span> Local Signup</a>
    </div>

</div>
</body>
</html>

Giờ chúng ta truy cập ứng dụng qua browser chúng ta sẽ thấy như thế này:

Chúng ta sẽ có tương tự với trang đăng nhập.

Form đăng nhập views/login.ejs

<!-- views/login.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <!-- load fontawesome -->
    <style>
        body {
            padding-top: 80px;
        }
    </style>
</head>
<body>
<div class="container">

    <div class="col-sm-6 col-sm-offset-3">

        <h1><span class="fa fa-sign-in"></span> Login</h1>

        <!-- hiển thị mọi tin nhắn được trả lại từ việc đăng nhập -->
        <% if (message.length > 0) { %>
        <div class="alert alert-danger"><%= message %></div>
        <% } %>

        <!-- LOGIN FORM -->
        <form action="/login" method="post">
            <div class="form-group">
                <label>Email</label>
                <input type="text" class="form-control" name="email">
            </div>
            <div class="form-group">
                <label>Password</label>
                <input type="password" class="form-control" name="password">
            </div>

            <button type="submit" class="btn btn-warning btn-lg">Login</button>
        </form>

        <hr>

        <p>Need an account? <a href="/signup">Signup</a></p>
        <p>Or go <a href="/">home</a>.</p>

    </div>

</div>
</body>
</html>

Form đăng ký views/signup.ejs

<!-- views/signup.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <!-- load fontawesome -->
    <style>
        body {
            padding-top: 80px;
        }
    </style>
</head>
<body>
<div class="container">

    <div class="col-sm-6 col-sm-offset-3">

        <h1><span class="fa fa-sign-in"></span> Signup</h1>

        <!-- hiển thị mọi tin nhắn được trả lại từ server cho việc đăng ký -->
        <% if (message.length > 0) { %>
        <div class="alert alert-danger"><%= message %></div>
        <% } %>

        <!-- LOGIN FORM -->
        <form action="/signup" method="post">
            <div class="form-group">
                <label>Email</label>
                <input type="text" class="form-control" name="email">
            </div>
            <div class="form-group">
                <label>Password</label>
                <input type="password" class="form-control" name="password">
            </div>

            <button type="submit" class="btn btn-warning btn-lg">Signup</button>
        </form>

        <hr>

        <p>Already have an account? <a href="/login">Login</a></p>
        <p>Or go <a href="/">home</a>.</p>

    </div>

</div>
</body>
</html>

Xác thực với Passport local

Giờ chúng ta sẽ tạo user model, cấu hình passport cho xác thực local và sử dụng cấu hình passport cho việc xử lý đăng nhập và đăng ký.

User Model

Chúng ta sẽ tạo một user model sử dụng cho toàn bộ series này. User của chúng ta sẽ có khả năng kết nối tới nhiều tài khoản mạng xã hội và tài khoản local. Với trường hợp local chúng ta sẽ dùng email và password. Với các tài khoản mạng xã hội, chúng ta sẽ dùng id và token và một số thông tin của người dùng.

Bạn có thể thay đổi một vài trường mà bạn muốn. Bạn có thể sử dụng username và password cho việc xác thực tài khoản local.

// app/models/user.js
// load những thư viện chúng ta cần
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');

// định nghĩ cấu trúc user model
var userSchema = mongoose.Schema({
    local: {
        email: String,
        password: String
    },
    facebook: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    twitter: {
        id: String,
        token: String,
        displayName: String,
        username: String
    },
    google: {
        id: String,
        token: String,
        email: String,
        name: String
    }

});

// methods ======================
// phương thực sinh chuỗi hash
userSchema.methods.generateHash = function (password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// kiểm tra password có hợp lệ không
userSchema.methods.validPassword = function (password) {
    return bcrypt.compareSync(password, this.local.password);
};

module.exports = mongoose.model('User', userSchema);

Chúng ta đã hoàn thiện user model. Chúng ta “băm” (hashing) mật khẩu của user trước khi lưu nó vào db. Điều này có nghĩa là chúng ta sẽ không phải lưu mã hash vào trong bảng user, bản thân nó sẽ tự giải quyết được vấn đề so sánh hash và plantext.

Chúng ta sẽ đi vào phần chính của bài hướng dẫn: Xác thực local với passport!

Cấu hình passport cho tài khoản local

Tất các cấu hình cho passport sẽ được viết ở file config/passport.js. Chúng ta code tách riêng phần này ra ngoài file server, giống như routes và config. Tôi đã tứng thấy nhiều người viết phần này ở những nơi khác nhau, thậm chí là trong file server. Tôi tin viết riêng ra một file sẽ “clean” và ngắn gọn hơn.

Cho tới giờ, chúng ta tạo ra một đối tượng passport ở trong server.js, và tiếp theo đẩy nó vào config/passport.js. Chúng ta sẽ cấu hình Strategy cho các trường hợp local, facebook, twitter, và google. Đây cũng là tệp chúng ta thể hiện các hàm serializeUser và deserializeUser để lưu trữ thông tin user trong session.

Tôi khuyên các bạn đọc qua tài liệu  passport docs để hiểu cách thức hoạt động của package này.

Xử lý request Đăng nhập/Đăng ký

Chúng ta sẽ xử lý đăng nhập và đăng ký ở trong file config/passport.js. Ban đầu trong nó sẽ như thế này:

// config/passport.js

// load những thứ chúng ta cần
var LocalStrategy = require('passport-local').Strategy;

// load  user model
var User = require('../app/models/user');

module.exports = function (passport) {

    // =========================================================================
    // passport session setup ==================================================
    // =========================================================================
    // required for persistent login sessions
    // passport needs ability to serialize and unserialize users out of session

    // used to serialize the user for the session
    passport.serializeUser(function (user, done) {
        done(null, user.id);
    });

    // used to deserialize the user
    passport.deserializeUser(function (id, done) {
        User.findById(id, function (err, user) {
            done(err, user);
        });
    });

    // =========================================================================
    // LOCAL SIGNUP ============================================================
    // =========================================================================

    passport.use('local-signup', new LocalStrategy({
            // mặc định local strategy sử dụng username và password,
            // chúng ta cần cấu hình lại
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true // cho phép chúng ta gửi reqest lại hàm callback
        },
        function (req, email, password, done) {
            // asynchronous
            // Hàm callback của nextTick chỉ được thực hiện khi hàm trên nó trong stack (LIFO) được thực hiện
            // User.findOne sẽ không được gọi cho tới khi dữ liệu được gửi lại
            process.nextTick(function () {

                // Tìm một user theo email
                // chúng ta kiểm tra xem user đã tồn tại hay không
                User.findOne({'local.email': email}, function (err, user) {
                    if (err)
                        return done(err);

                    if (user) {
                        return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
                    } else {

                        // Nếu chưa user nào sử dụng email này
                        // tạo mới user
                        var newUser = new User();

                        // lưu thông tin cho tài khoản local
                        newUser.local.email = email;
                        newUser.local.password = newUser.generateHash(password);

                        // lưu user
                        newUser.save(function (err) {
                            if (err)
                                throw err;
                            return done(null, newUser);
                        });
                    }

                });

            });

        }));

};

Giờ chúng ta đã cung cấp một Strategy tới passport với tên local-signup. Chúng ta sẽ sử dụng Strategy này cho việc xử lý form đăng ký. Mở lại file app/reoutes.js và thêm vào xử lý cho request POST /signup

// app/routes.js
...

    // Xử lý thông tin khi có người đăng ký
    app.post('/signup', passport.authenticate('local-signup', {
        successRedirect: '/profile', // chuyển hướng tới trang được bảo vệ
        failureRedirect: '/signup', // trở lại trang đăng ký nếu có lỗi
        failureFlash: true // allow flash messages
    }));

...

Có nhiều điều bạn có thể làm ở đây. Thay vì chỉ định một đường dẫn cho successRedirect, bạn có thể sử dụng một callback và thực hiện những công việc theo yêu cầu của ứng dụng của bạn. Đây là câu trả lời rất rõ ràng stackoverflow answer cho việc quản lý lỗi. Nó giải thích cách callback done() làm việc và làm thế nào để custom một route.

Kiểm tra đăng ký

Với những gì chúng ta đã cấu hình cho passport, chúng ta có thể bỏ comment một dòng ở file server.js. Nó sẽ sử dụng config của chúng ta và form đăng ký có thể hoạt động.

// server.js
...

    // bỏ comment dòng này
    require('./config/passport')(passport); // pass passport for configuration

...

Giờ chúng ta đã có passport, routes, và địa chỉ được bảo vệ (/profile), tiến hành test form đăng ký. Trên trình duyệt bạn truy cập theo địa chỉ http://localhost:8080/signup   điền thông tin vào form và submit.

Nếu mọi thứ theo kế hoạch, bạn sẽ được đăng nhập, thông tin user sẽ được lưu trong session và bạn được chuyển tới trang /profile (hiện tại đây là trang trắng, vì chúng ta chưa chuẩn bị view này).

Nếu bạn nhìn vào db, chúng ta sẽ thấy thông tin user được tạo:

Để quản lý mongodb các bạn có thể dùng Robomongo. Cài đặt và kết nối vào db của mình.

Với những user có thể đăng ký, giờ chúng ta làm chức năng đăng nhập.

Đăng nhập

Việc này tương đối giống với việc đăng ký. Chúng ta sẽ thêm một Strategy vào file config/passport.js và sửa file app/routes.js

// config/passport.js
...

    // =========================================================================
    // LOCAL LOGIN =============================================================
    // =========================================================================
    passport.use('local-login', new LocalStrategy({
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true
        },
        function (req, email, password, done) { // callback với email và password từ html form

            // find a user whose email is the same as the forms email
            // we are checking to see if the user trying to login already exists
            // tìm một user với email
            // chúng ta sẽ kiểm tra xem user có thể đăng nhập không
            User.findOne({'local.email': email}, function (err, user) {
                if (err)
                    return done(err);

                // if no user is found, return the message
                if (!user)
                    return done(null, false, req.flash('loginMessage', 'No user found.'));

                // if the user is found but the password is wrong
                if (!user.validPassword(password))
                    return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // thông báo lỗi chỉ này chỉ dùng khi dev

                // all is well, return successful user
                return done(null, user);
            });

        })
    );
...
// app/routes.js
...
    // Xử lý thông tin khi có người thực hiện đăng nhập
    app.post('/login', passport.authenticate("local-login", {
        successRedirect : '/profile',
        failureRedirect : '/login',
        failureFlash : true
    }));
...

Nếu bạn cố đăng nhập với một email chưa được đăng ký vào hệ thống, bạn sẽ nhận được thông báo lỗi. Tương tự với password.

Hiển thị thông tin user ở trang /profile

Giờ chúng ta đã có phương thức đăng ký và đăng nhập. Nếu một user đăng nhập thành công họ sẽ chuyển đến trang profile. Nếu không thành công sẽ được chuyển về trang chủ.

Vấn đề cuối cùng cho bài viết này là tạo trang profile.

<!-- views/profile.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <style>
        body {
            padding-top: 80px;
            word-wrap: break-word;
        }
    </style>
</head>
<body>
<div class="container">

    <div class="page-header text-center">
        <h1><span class="fa fa-anchor"></span> Profile Page</h1>
        <a href="/logout" class="btn btn-default btn-sm">Logout</a>
    </div>

    <div class="row">

        <!-- LOCAL INFORMATION -->
        <div class="col-sm-6">
            <div class="well">
                <h3><span class="fa fa-user"></span> Local</h3>

                <p>
                    <strong>id</strong>: <%= user._id %><br>
                    <strong>email</strong>: <%= user.local.email %><br>
                    <strong>password</strong>: <%= user.local.password %>
                </p>

            </div>
        </div>

    </div>

</div>
</body>
</html>

Sau khi user đăng nhập họ sẽ thấy thông tin của họ. Đó là thông tin được lấy từ session và truyền qua view ở file app/routes.js

Chúng ta sẽ cung cấp một link để đăng xuất(logout)

Trong những bài tiếp theo chúng ta sẽ sử dụng cấu trúc đã xây dựng ở bài này để thực hiện xác thực với Facebook, Twitter và Google. Sau đó chúng ta sẽ xem tất cả chúng làm việc như thế nào trên cũng một ứng dụng. User sẽ đăng nhập với một loại tài khoản và liên kết với những tài khoản khác.