Xác thực đăng nhập bằng Passport – Google

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 (Bạn đang ở đây)
  5. Liên kết các tài khoản

Chào mừng tới phần 4 của series Xác thực đăng nhập bằng Passport. Chúng ta sẽ sử dụng nền tảng của ứng dụng chúng ta đã xây dựng từ những bài trước để thêm tính năng xác thực với Google. Chúng ta đã một cấu trúc chương trình đầy đủ với các gói thư viện cần dùng, user model, cấu hình ứng dụng và các views.

Chúng ta đã tạo ra một chương trình với cấu trúc khá rõ ràng và ngắn gọn, ở bài này những gì chúng ta phải làm sẽ là tạo một Google app, sửa các file sau:

Lưa thông tin cấu hình Google app: ClientId, Secret config/auth.js
Cấu hình Google Strategy cho Passport config/passport.js
Khởi tạo route app/routes.js
Cập nhật view views/

Trong bài không nó rõ các bước tạo một App Google, tôi mặc định các bạn đã làm được bước này. Chúng ta sẽ đi thẳng tới việc cấu hình Passport Google Strategy.

Xác thực với Google sử dụng Passport

Tạo ứng dụng Google

Tiến hành khởi tạo một ứng dụng Google tại đường dẫn Cloud API Console. Mình nghĩ việc này không quá phức tạp. Ứng dụng google có thể được tìm thấy ở thư mục Project > APIs & auth. Chúng ta sẽ cần các thông tin ClientId, Secret, và callback URL.

Ở những bài trước chúng ta đã khởi tạo các đường dẫn trả về có dạng: http://localhost:8080/auth/google/callback.

Nếu Google không chấp nhận http://localhost:8080, hãy sử dụng http://127.0.0.1:8080.

Giờ thêm thông tin về ClientId và Secret vào file auth.js để sử dụng cho việc xác thực với Google:

// config/auth.js
module.exports = {
    'facebookAuth': {
        'clientID': '182734854751xxxx', // App ID của bản
        'clientSecret': '7e7b945a5df7c7da047ee5577058xxxx', // App Secret của bạn
        'callbackURL': 'http://localhost:8080/auth/facebook/callback'
    },
    'twitterAuth': {
        'consumerKey': 'ieCPgRLTTijLBvHwQs40xxxx',
        'consumerSecret': 'IY0bJrhRU9Pn4Rqf8AtDP7iclsSoEHW62OL7f2oln6vLCgxxxx',
        'callbackURL': 'http://localhost:8080/auth/twitter/callback'
    },
    'googleAuth': {
        'clientID': '461436299130-k2nqk0a41m13rojbxxxxxxxbmlfq8c0atf2.apps.googleusercontent.com',
        'clientSecret': 'CC_UcyDamlxxxxx3N9aj4xdi',
        'callbackURL': 'http://localhost:8080/auth/google/callback'
    }
};

Customizing màn hình xin quyền xác thực ứng dụng với Google

Google cho phép bạn thay đổi màn hình đăng nhập mặc định, bạn có thể custom với logo và text của bạn.

Phần này có thể tìm thấy ở ngay trang mà bạn lấy ClientId và Secret.

Cấu hình Google Strategy cho Passport config/passport.js

Giờ để ứng dụng của chúng ta có thể sử dụng xác thực của Google, chúng ta sẽ phải cấu hình Google Strategy. Strategy này sẽ sử dụng xác thực của Google và xử lý dữ liệu khi xác thực Google thành công.

Chúng ta sẽ dùng package passport-google-oauth của Jared Hanson để thực hiện việc xác thực với OAuth2.

Chúng ta đã có code cho Local Strategy, Facebook Strategy và Twitter Strategy ở file này, chúng ta sẽ thêm phần Google Strategy ở ngay bên dưới. Luồng xử lý dữ liệu tương tự cho Facebook và Twitter:

// config/passport.js

// load những thứ chúng ta cần
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
var TwitterStrategy = require('passport-twitter').Strategy;
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

// Lấy thông tin những giá trị auth
var configAuth = require('./auth');

// 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);
        });
    });

    // code for login (use('local-login', new LocalStategy))
    // code for signup (use('local-signup', new LocalStategy))
    // code for facebook (use('facebook', new FacebookStrategy))
    // code for twitter (use('twitter', new TwitterStrategy))    

    // =========================================================================
    // GOOGLE ==================================================================
    // =========================================================================
    passport.use(new GoogleStrategy({
            clientID: configAuth.googleAuth.clientID,
            clientSecret: configAuth.googleAuth.clientSecret,
            callbackURL: configAuth.googleAuth.callbackURL,
        },
        function (token, refreshToken, profile, done) {
            process.nextTick(function () {

                // // tìm trong db xem có user nào đã sử dụng google id này chưa
                User.findOne({'google.id': profile.id}, function (err, user) {
                    if (err)
                        return done(err);

                    if (user) {

                        // if a user is found, log them in
                        return done(null, user);
                    } else {
                        // if the user isnt in our database, create a new user
                        var newUser = new User();

                        // set all of the relevant information
                        newUser.google.id = profile.id;
                        newUser.google.token = token;
                        newUser.google.name = profile.displayName;
                        newUser.google.email = profile.emails[0].value; // pull the first email

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

        }));
};

Tiếp đến chúng ta viết routes cho Strategy mới này.

Routes app/routes.js

Chúng ta sẽ cần 2 routes:

  • /auth/google: Gửi yêu cầu của chúng ta tời Google để yêu cầu xác thực.
  • /auth/google/callback: Google gửi lại ứng dụng của chúng ta chuỗi token và thông tin user.
// app/routes.js
...
    // =====================================
    // GOOGLE ROUTES =======================
    // =====================================
    // send to google to do the authentication
    // profile gets us their basic information including their name
    // email gets their emails
    app.get('/auth/google', passport.authenticate('google', {scope: ['profile', 'email']}));

    // the callback after google has authenticated the user
    app.get('/auth/google/callback',
        passport.authenticate('google', {
            successRedirect: '/profile',
            failureRedirect: '/'
        }));
...

Các route của chúng ta rất đơn giản, yêu cầu xác thực và xử lý dữ liệu được gọi lại.

Hiển thị thông tin user index.ejs, profile.ejs

Chúng ta đã có các routes, chúng ta chỉ cần cung cấp cho người dùng một cái nút để đăng nhập với Google. Tiếp theo, sau khi họ đã xác thực và được chuyên tới trang riêng tư – profile, chúng ta sẽ hiển thị thông tin của user ở đây.

Nút đăng nhập với Google

Chúng ta sẽ thêm nút đăng nhập với Google vào file views/index.ejs

<!-- views/index.ejs -->
...
        <a href="/auth/twitter" class="btn btn-info"><span class="fa fa-twitter"></span> Twitter</a>
        <a href="/auth/google" class="btn btn-danger"><span class="fa fa-google-plus"></span> Google</a>

...

Chúng ta đã có nút để xác thực với Google. Khi user click vào nút đó, họ sẽ truy cập tới route /auth/google nơi họ sẽ được chuyển tới Google Strategy. Họ sẽ gửi tới Google một yêu cầu xác thực.

Nếu một user nhấn Cancel, họ sẽ được chuyển về trang chủ. Đó là cài đặt được đặt ở failureRedirect của route /auth/google/callback. Nếu việc xác thực thành công, thông tin user sẽ được lưu lại và họ được chuyển tới trang /profile .

Trang Profile views/profile.ejs

Cuối cùng chúng ta sẽ hiển thị thông tin user ở trang profile.

Chúng ta có thể thấy user ở trong db:

Giờ chúng ta hiển thị thông tin ra ngoài trang profile:

<!-- views/profile.ejs -->
...
        <!-- GOOGLE INFORMATION -->
        <div class="col-sm-6">
            <div class="well">
                <h3 class="text-danger"><span class="fa fa-google-plus"></span> Google</h3>

                <p>
                    <strong>id</strong>: <%= user.google.id %><br>
                    <strong>token</strong>: <%= user.google.token %><br>
                    <strong>email</strong>: <%= user.google.email %><br>
                    <strong>name</strong>: <%= user.google.name %>
                </p>

            </div>
        </div>
...

All done!

Như những bài trước, bài này hướng dẫn việc xác thực bằng các tài khoản mạng xã hội sử dụng phương thức xác thực OAuth 2.0.

Trong bài viết tiếp theo cũng là cuối cùng của series, chúng ta sẽ thực hiện liên kết các tài khoản vào một.