概要
FacebookのOAuth2.0を利用した認証を使うことがあったのでまとめました。
Node.jsなのでpassportというライブラリを使用します。
環境
- Node.js 5.0.0
- Express 4.13.1
- npm 3.4.0
事前準備
以下のFacebookページでOAuth用のアプリを用意してください。
こんな感じになります。
App Domains
には許可するURLドメインを追加していきます。
ここに登録されていないコールバックURLは許可されません。
これは下のサイトURL
のドメインと異なってはいけないです。
ただし異なるサブドメインの登録はできます。
例)
dev.example.com, local.example.com
なので1つのアプリで複数環境(dev, stg, prdなど)を扱うことは可能です。
実装
完成物
最初に今回の完成形を貼っておきます。
基本的にExpressのスケルトンコードを利用します。
$ npm install -g express-generator $ express oauth-facebook
これに以下のようなconfig
、middleware
などを追加していきます。
フォルダツリー
完成物のフォルダ構造です。
. ├── app.js ├── bin │ └── www ├── config │ └── local.js ├── lib │ └── passport.js ├── middleware │ └── auth.js ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets ├── routes │ ├── auth.js │ ├── index.js │ └── users.js └── views ├── error.jade ├── index.jade └── layout.jade
各ファイルの説明
主な部分を簡単に説明します。
フォルダ、ファイル | 説明 |
---|---|
config | facebookのclientIDなどを記述 |
lib | passportの処理をまとめて記述 |
middleware | 認証済みかどうかのチェック処理 |
routes/index | ログインページ |
routes/auth | OAuthの処理。コールバック処理など |
routes/users | 認証済みの場合のみ開けるページ |
package.json
今回使うsession
、passport
に関するライブラリを追記します。
"express-session": "*", "passport": "0.3.0", "passport-facebook": "2.0.0",
config/local.json
先ほどFacebookで作ったアプリのclientID
、clientSecret
を記述します。
callbackURL
は先ほど登録したドメインのものにしましょう。
scope
には使用したいFacebookの情報の許可範囲を指定します。
この値はgoogle, twitter各サービスによって異なるのでそれぞれ調べましょう。
今回はログインだけなので特に要らないですが、書き方の例として以下のようなscopeをつけます。
'use strict'; let config = {}; config.oauth = { facebook: { clientID: 'your_client_id', clientSecret: 'your_client_secret', callbackURL: 'http://example.com:3000/auth/callback', scope: ['email', 'user_friends', 'user_birthday', 'user_location'] } } module.exports = config;
lib/passport.js
最低限passportを使う上での処理を書きます。
引数のpassport
はapp.js
で渡します。
ここで渡すprofile
は、認証後req.session.passport
に保持されます。
'use strict'; const config = require('../config/local'); const FacebookStrategy = require('passport-facebook').Strategy; let initPassport = function(passport) { passport.use(new FacebookStrategy(config.oauth.facebook, (accessToken, refreshToken, profile, done) => { // asynchronous verification, for effect... process.nextTick(() => { return done(null, profile); }); })); passport.serializeUser((user, done) => { done(null, user); }); passport.deserializeUser((obj, done) =>{ done(null, obj); }); }; module.exports = initPassport;
app.js
以下の内容を追記します。
ライブラリ読み込み
const routes = require('./routes/index'); const auth = require('./routes/auth'); const users = require('./routes/users'); const session = require('express-session'); const passport = require('passport'); const authorized = require('./middleware/auth');
ミドルウェア登録
app.use(session({ secret : 'cuaM6reezu7aechooLoh', resave : false, saveUninitialized : true, })); app.use(passport.initialize()); app.use(passport.session());
ルーティング
/me
ページは認証済みでないと入れないようにmiddlewareをはさみます。
// passport require('./lib/passport')(passport); // routing app.use('/', routes); app.use('/auth', auth); app.use('/me', authorized, users);
middleware/auth.js
req.isAuthenticated()
というメソッドで認証済みか確認することができます。
'use strict'; let authorized = function(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/'); }; module.exports = authorized;
views/index.jade
ログインするためのリンクだけ追記します。
extends layout block content h1= title p Welcome to #{title} a(href="/auth/") Sign In with Facebook
routes/auth.js
OAuth2.0はCSRF脆弱性があるので、state
パラメータで検証する必要があります。
先ほどreq.session.passport
にprofile
が保持されると言いましたが、初回の/callback
ではまだ認証が終わってないのでセッションには入ってません。
req.isAuthenticated()
が通った状態でならセッションに入っています。
'use strict'; const express = require('express'); const crypto = require('crypto'); const passport = require('passport'); let router = express.Router(); router.get('/', (req, res, next) => { if (!req.session.state) { var current_date = (new Date()).valueOf().toString(); var random = Math.random().toString(); var hash = crypto.createHash('sha1').update(current_date + random).digest('hex'); req.session.state = hash } passport.authenticate('facebook', { state: req.session.state })(req, res, next); }); router.get('/callback', (req, res, next) => { if (!req.session.state) { return res.status(400).send({err: 'no state parameter'}); } // CSRF verification if (req.query.state !== req.session.state) { return res.status(400).send({err: 'invalid state parameter'}); } passport.authenticate('facebook', { failureRedirect: '/', successRedirect: '/me' })(req, res, next); }); module.exports = router;
routes/users.js
ログインしたことが分かるように、title
を標準のExpress
からOAuth
に変更しておきます。
'use strict'; const express = require('express'); let router = express.Router(); /* GET users listing. */ router.get('/', (req, res, next) => { res.render('index', { title: 'OAuth' }); }); module.exports = router;
動作確認
へアクセスします。
ログインリンクをクリックすると以下のFacebookの認可ページに飛ばされます。
その際のリンクは以下のようになります。
redirect_uri
やstate
パラメータがちゃんと付いてますね。
問題無ければ以下のように/me
ページヘ飛ばされます。
以上です。お疲れ様でした。