Hướng dẫn làm login bằng facebook với nodejs

Chào mừng tới phần 2 của series Xác thực đăng nhập bằng Passport. Ở bài này chúng ta sẽ sử dụng xác thực với facebook cho ứng dụng. Sau khi hoàn thành phần 1, 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.

Hướng dẫn làm login bằng facebook với nodejs

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 Facebook app, sửa các file sau:

Lưa thông tin cấu hình Facebook app: ClientId, Secret`config/auth.js`Cấu hình Facebook 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 Facebook, tôi mặc định các bạn đã làm được bước này.

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

Tạo ứng dụng Facebook

Đầu tiên, chúng ta vào trang Facebook Developers Portal và tạo một ứng dụng. Các thông tin chúng ta cần lấy là AppId và AppSecret để thực hiện việc xác thực với Facebook.

Hướng dẫn làm login bằng facebook với nodejs

Cài đặt Callback URL là http://localhost:8080/auth/facebook/callback . Đây sẽ là url Facebook sẽ gọi lại khi người dùng đã xác thực thành công ứng dụng.

Ứng dụng thật(Public) với Sanbox(bản thử nghiệm): Nếu bạn để ứng dụng Facebook của bạn ở chế độ Sanbox thì chỉ bạn mới có thể xác thực được. Public thì mọi người đều có thể xác thực.

Giờ bạn đã có thông tin về Facebook app: AppId và Secret, hãy điền nó vào file config. Chúng ta cũng sẽ để những trường tương tự cho Twitter và Google.

// config/auth.js module.exports = {

'facebookAuth': {
    'clientID': '182734854xxxxxxx', // App ID của bản
    'clientSecret': '7e7b945a5df7c7da047ee5577xxxxxxx', // App Secret của bạn
    'callbackURL': 'http://localhost:8080/auth/facebook/callback'
},
'twitterAuth': {
    'consumerKey': 'your-consumer-key-here',
    'consumerSecret': 'your-client-secret-here',
    'callbackURL': 'http://localhost:8080/auth/twitter/callback'
},
'googleAuth': {
    'clientID': 'your-secret-clientID-here',
    'clientSecret': 'your-client-secret-here',
    'callbackURL': 'http://localhost:8080/auth/google/callback'
}
};

Giờ chúng ta sẽ thiết lập Facebook Strategy.

Cấu hình Facebook 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 Facebook, chúng ta sẽ phải cấu hình Facebook Strategy. Strategy này sẽ sử dụng xác thực của Facebook và xử lý dữ liệu khi xác thực Facebook thành công.

Chúng ta đã có code cho Local Strategy ở file này, chúng ta sẽ thêm phần Facebook Strategy ở ngay bên dưới.

// config/passport.js // load những thứ chúng ta cần var LocalStrategy = require('passport-local').Strategy; var FacebookStrategy = require('passport-facebook').Strategy; // 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))
// =========================================================================
// FACEBOOK ================================================================
// =========================================================================
passport.use(new FacebookStrategy({
        // điền thông tin để xác thực với Facebook.
        // những thông tin này đã được điền ở file auth.js
        clientID: configAuth.facebookAuth.clientID,
        clientSecret: configAuth.facebookAuth.clientSecret,
        callbackURL: configAuth.facebookAuth.callbackURL,
        profileFields: ['id','displayName','email','first_name','last_name','middle_name']
    },
    // Facebook sẽ gửi lại chuối token và thông tin profile của user
    function (token, refreshToken, profile, done) {
        // asynchronous
        process.nextTick(function () {
            // tìm trong db xem có user nào đã sử dụng facebook id này chưa
            User.findOne({'facebook.id': profile.id}, function (err, user) {
                if (err)
                    return done(err);
                // Nếu tìm thấy user, cho họ đăng nhập
                if (user) {
                    return done(null, user); // user found, return that user
                } else {
                    // nếu chưa có, tạo mới user
                    var newUser = new User();
                    // lưu các thông tin cho user
                    newUser.facebook.id = profile.id;
                    newUser.facebook.token = token;
                    newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; // bạn có thể log đối tượng profile để xem cấu trúc
                    newUser.facebook.email = profile.emails[0].value; // fb có thể trả lại nhiều email, chúng ta lấy cái đầu tiền
                    // lưu vào db
                    newUser.save(function (err) {
                        if (err)
                            throw err;
                        // nếu thành công, trả lại user
                        return done(null, newUser);
                    });
                }
            });
        });
    }));
};

Profile: Object chứ thông tin user của dịch vụ(Facebook, Twitter, Google) sẽ có cấu trúc thông tin khác nhau, nhưng Passport đã xử lý về một đối tượng để dễ xử lý user profile doc.

Chúng ta sẽ sử dụng Strategy cho việc xác thực với Facebook và xử lý khi Facebook trả lại thông tin. Chúng ta sẽ lưu thông tin user và chuỗi token để truy cập các dịch vụ của Facebook.

Giờ chúng ta sẽ thiết lập các routes.

Routes app/routes.js

Hãy nhớ khi chúng ta thiết lập ứng dụng, đối tượng passport đã được chuyển từ server.js tới file config/passport.js. Cuối cùng nó được chuyển qua các file routes. Đó là lý do tại sao chúng ta chỉ thêm Facebook Stategy ở file config mà nó lại hoạt động được ở file route.

Ở phần này chúng ta cần 2 routes: Một cho việc yêu cầu xác thực với Facebook, một cho việc xử lý callback.

  • /auth/facebook: Gửi yêu cầu của chúng ta tời Facebook để yêu cầu xác thực.
  • /auth/facebook/callback: Facebook gửi lại ứng dụng của chúng ta chuỗi token và thông tin user.

// app/routes.js ...

// =====================================
// FACEBOOK ROUTES =====================
// =====================================
// yêu cầu xác thực bằng facebook
app.get('/auth/facebook', passport.authenticate('facebook', {scope: ['email']}));
// xử lý sau khi user cho phép xác thực với facebook
app.get('/auth/facebook/callback',
    passport.authenticate('facebook', {
        successRedirect: '/profile',
        failureRedirect: '/'
    })
);
...

Mặc định, Facebook sẽ trả lại thông tin user, nhưng không có thông tin email. Chúng ta sẽ thêm một scope là email. Bạn có thể thêm vào những scope khác để lấy thêm thông tin.

Khi xây dựng ứng dụng đăng nhập với Facebook, cố gắng chỉ sử dụng những thông tin mà bạn cần. Như vậy người dùng mới có thể thoải mái với quyền riêng tư khi sử dụng ứng dụng của bạn.

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 Facebook. 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 Facebook

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

<! views/index.ejs > ...

    <a href="/signup" class="btn btn-default"><span class="fa fa-user"></span> Local Signup</a>
    <a href="/auth/facebook" class="btn btn-primary"><span class="fa fa-facebook"></span> Facebook</a>
...

Hướng dẫn làm login bằng facebook với nodejs

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

Hướng dẫn làm login bằng facebook với nodejs

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

// config/passport.js // load những thứ chúng ta cần var LocalStrategy = require('passport-local').Strategy; var FacebookStrategy = require('passport-facebook').Strategy; // 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))
// =========================================================================
// FACEBOOK ================================================================
// =========================================================================
passport.use(new FacebookStrategy({
        // điền thông tin để xác thực với Facebook.
        // những thông tin này đã được điền ở file auth.js
        clientID: configAuth.facebookAuth.clientID,
        clientSecret: configAuth.facebookAuth.clientSecret,
        callbackURL: configAuth.facebookAuth.callbackURL,
        profileFields: ['id','displayName','email','first_name','last_name','middle_name']
    },
    // Facebook sẽ gửi lại chuối token và thông tin profile của user
    function (token, refreshToken, profile, done) {
        // asynchronous
        process.nextTick(function () {
            // tìm trong db xem có user nào đã sử dụng facebook id này chưa
            User.findOne({'facebook.id': profile.id}, function (err, user) {
                if (err)
                    return done(err);
                // Nếu tìm thấy user, cho họ đăng nhập
                if (user) {
                    return done(null, user); // user found, return that user
                } else {
                    // nếu chưa có, tạo mới user
                    var newUser = new User();
                    // lưu các thông tin cho user
                    newUser.facebook.id = profile.id;
                    newUser.facebook.token = token;
                    newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; // bạn có thể log đối tượng profile để xem cấu trúc
                    newUser.facebook.email = profile.emails[0].value; // fb có thể trả lại nhiều email, chúng ta lấy cái đầu tiền
                    // lưu vào db
                    newUser.save(function (err) {
                        if (err)
                            throw err;
                        // nếu thành công, trả lại user
                        return done(null, newUser);
                    });
                }
            });
        });
    }));
};

0. 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 .