passportモジュールでfacebook認証

#passportとは
passportはNode.jsで利用できる認証ミドルウェア（モジュール）です。
passportを利用することで、アプリに簡単にOAuth認証を組み込むことができます。

OAuth認証に関して、こちらの記事がすごく分かりやすいので読んでみてください。
[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)

passportの凄いところは、FacebookやTwitter、Googleなど、多くのアカウント認証を利用できる点です。

今回は、ExpressというNode.jsのフレームワークがグローバルインストールされている前提で進んでいきます。
参考：[Expressフレームワークのインストールと簡単な使い方](https://qiita.com/tarotaro1129/items/e02fbb911dc4af412ad0)

#passportを利用して、Facebook認証をしてみる
####Facebook Developersでアプリを作成
まずは、Facebook認証を利用するために、https://developers.facebook.com/ からアプリケーションの作成を行なってください。
![スクリーンショット 2019-12-02 17.52.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/b45d26fb-cadc-9a96-5de7-84d7b1a990fb.png)
作成ができたら、アプリの設定ページで以下のように、アプリIDとapp secretが作成されています。
![スクリーンショット 2019-12-02 17.53.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/4e51489c-700d-7cbf-5830-ab1adab905f5.png)
このアプリIDとapp secretは、後で利用することになります。

####passportで使うモジュールのインストール
続いて、passportモジュールのインストールを始めます。
以下のコマンドを入力してください。

```console:console
$ express --view=pug passport-demo
$ cd passport-demo
$ npm install
$ npm init -y
$ npm install passport
$ npm install passport-facebook
$ npm install express-session
```
>・1行目でプロジェクトを行う`passport-demo`ディレクトリを作成しています。このディレクトリの名前はなんでも大丈夫です。
・2行目は、1行目で作成した`passport-demo`ディレクトリに移動しています。
・3行目は依存モジュールのインストールを行なっています。
・4行目は、初期化処理を行い、`package.json`を生成しています。
`npm init`を行うと普通はどういうパッケージにするか質問がされるのですが、`-y`オプションを作ることで、すべてyesの回答となり、その質問を省略することができます。
・5、6行目で`passport`モジュールと`passport-facebook`モジュールをインストールしています。
・7行目は、`Express`でセッションを利用できるようにするためのモジュールです。認証した結果をサーバーがセッション情報として保存してくれます。

念の為、この段階で以下のコマンドを実行し、expressがうまく起動できているか確認します。

```console:console
$ PORT=8000 npm start
```
http://localhost:8000/ にアクセスしてみてください。
Express
Welcome to Express
という画面が表示されたら、問題ありません。

####必要なディレクトリ・ファイルを作る
今回は以下のようなファイル構造にします。

```
// *は新しく作成するファイル
passport-demo
|- package.json
|- package-lock.json
|- app.js
|- /routes
  |- index.js
  |- login.js *
  |- logout.js *
|- /bin
  |- www
|- /views
  |- login.pug *
  |- index.pug
  |- layout.pug
|- /public
|- /node_modules
```
>・`app.js`は`passport`モジュールの設定などを書き込むファイルです。
・`localhost:8000/`にアクセスしたときの処理を書き込みます。
・`login.js`と`logout.js`は、`/login`と`/logout`にアクセスしたときの処理を書き込みます。
・`www`はサーバーの起動などを担当します。
・`login.pug`は`/login`にアクセスした時のログイン画面を表示します。
・`index.pug`は`/`にアクセスした時の画面を表示します。
・`layout.pug`は`login.pug`と`index.pug`の基本となる表示を担当します。

続いて、以上のような認証の際に必要なファイルやディレクトリの作成、その記述を行なっていきます。すでに存在しているファイルの作成は不要なので、以下のファイルだけを作成します。

```console:console
$ touch routes/login.js
$ touch routes/logout.js
$ touch views/login.pug
```

####ファイルに処理の記述を行う。
続いては、処理の記述を行なっていきます。
まずは、今あるapp.jsの記述を削除して、app.jsに以下の処理を記述してください。

```JS:app.js
var createError = require('http-errors');
//expressモジュールの読み込み
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

//passportモジュールの読み込み
var passport = require('passport');
//passport-facebookモジュールの読み込み
var Strategy = require('passport-facebook').Strategy;

//先ほど作成したアプリIDを変数に代入
var FACEBOOK_APP_ID = '44923726XXXXXX';
//先ほど作成したapp secretを変数に代入
var FACEBOOK_APP_SECRET = '170dde4751f2ee9d44140b8826457b63';

//passportモジュールによるシリアライズの設定
passport.serializeUser(function(user, done) {
  done(null, user);
});
//passportモジュールによるデシリアライズの設定
passport.deserializeUser(function(obj, done) {
  done(null, obj);
});

////passport-facebookモジュールのStarategy設定
passport.use(new Strategy({
  clientID: FACEBOOK_APP_ID,
  clientSecret: FACEBOOK_APP_SECRET,
  //facebook認証をするためのページの設定(固定)
  callbackURL: "http://localhost:8000/auth/facebook/callback",
  //プロフィールのどの情報を受け取ることができるかの設定
  profileFields: ['id', 'displayName']
},
  function (accessToken, refreshToken, profile, done) {
    //認証後にdone関数を返すために、process.nextTick関数を利用している
    process.nextTick(function () {
      return done(null, profile);
    });
  }
));

//index.jsの処理を利用するために変数に代入
var indexRouter = require('./routes/index');
//users.jsの処理を利用するための処理を変数に代入
var usersRouter = require('./routes/users');
//login.jsの処理を利用するための処理を変数に代入
var loginRouter = require('./routes/login');
//logout.jsの処理を利用するための処理を変数に代入
var logoutRouter = require('./routes/logout');

//expressモジュールを利用するためにapp変数に代入
var app = express();

//アプリケーションで利用するミドルウェアの設定
app.use(require('morgan')('combined'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// viewsディレクトリの設定
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

//passportのの初期化
app.use(passport.initialize());
//ログイン後のセッション管理の設定
app.use(passport.session());

//localhost:8000/にアクセスした時にindexRouterの処理がなされる設定
app.use('/', indexRouter);
//localhost:8000/usersにアクセスした時にusersRouterの処理がなされる設定
app.use('/users', usersRouter);

//localhost:8000/auth/facebookにGETアクセスした時に認証リクエストを行う設定
app.get('/auth/facebook',
  passport.authenticate('facebook')
);
//localhost:8000/auth/facebook/callbackにGETアクセスした時に処理が行われる設定
app.get('/auth/facebook/callback',
　//処理が失敗した時のリダイレクト先の設定
  passport.authenticate('facebook', {failureRedirect: '/login' }),
  function(req, res) {
    //処理が成功した時のリダイレクト先の設定
    res.redirect('/');
  });

//localhost:8000/loginにアクセスした時にloginRouter処理がなされる設定
app.get('/login', loginRouter);

//localhost:8000/logoutにアクセスした時にlogoutRouter処理がなされる設定
app.get('/logout', logoutRouter);


//404エラーの処理設定
app.use(function(req, res, next) {
  next(createError(404));
});

// エラー処理の設定
app.use(function(err, req, res, next) {
  // ローカル環境のみ表示されるエラーの設定
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // エラーページを表示する設定
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

```
> `var FACEBOOK_APP_ID = '4492xxxxxxxx'; 
 var FACEBOOK_APP_SECRET = '170dde4751f2exxxxxxxxxx';` は、[先ほど作成した](https://qiita.com/drafts/af7e4efcdec356f0bed0/edit#facebook-developers%E3%81%A7%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E6%88%90)、アプリIDとapp secretをそれぞれ代入します。


続いて、login.jsとlogout.js、そしてindex.jsに記述していきます。

```JS:login.js
'use strict';
//expressの読み込み
var express = require('express');
//expressでルーターを使う設定
var router = express.Router();

//localhost:8000/loginにアクセスした際に、login.pugがレンダリングされる処理
router.get('/login', function(req, res) {
  res.render('login');
});

//モジュールのエキスポート
module.exports = router;
```

```JS:logout.js
'use strict';
//expressの読み込み
var express = require('express');
//expressでルーターを使う設定
var router = express.Router();

//localhost:8000/logoutにアクセスした際に、ログアウトされ、
//localhost:8000/にリダイレクトされる処理
router.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});

//モジュールのエキスポート
module.exports = router;
```

```JS:index.js
'use strict';
var express = require('express');
var router = express.Router();

//localhost:8000/にアクセスした際に、index.pugがレンダリングされ、
//index.pug内でtitleとuserが使えるようになる処理
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express', user: req.user });
});

module.exports = router;
```
これでモジュールの処理設定が完了しました。
続いて画面の設定です。
上の処理に合わせて、画面設定をしていきます。


```pug:index.pug
extends layout

block content
  h1= title
  p Welcome to #{title}
  if user
    p Hello, #{user.displayName}
    a(href="/logout") Logout
  else
    a(href="/login") Login

```
>`#{title}`にはindex.jsで渡した、'Express'という文字が入り、
`{user.displayName}`の`user`には、req.userが入ります。
req.userは、`app.js`のStarategy設定で`displayName`をプロフィールから受け取れるようにしたので、`displayName`が利用できます。

```Jade:login.pug
extends layout

block content
  a(href="/auth/facebook") Login with Facebook
```
>ログインするための`a(href="/auth/facebook")`を追加しました。


####ログインとログアウトをしてみる
これで処理か完成しました。コンソールにて以下のコマンドを入力し、

```console:console
PORT=8000 npm start
```
以下のURLにアクセスしてみてください。
http://localhost:8000/ 

アクセスできたら、以下の画面が表示されると思います。
![スクリーンショット 2019-12-03 16.53.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/12b711a7-b43a-e1d1-7593-25879139bf70.png)

ここで`Login`をクリックします。
![スクリーンショット 2019-12-03 16.53.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/116fdf32-bb13-244f-f334-987299ced27f.png)

すると、`/login`に移動するので、`Login with Facebook`をクリックします。
![スクリーンショット 2019-12-03 16.54.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/a6bce36c-4486-44e9-ca21-c7f10bdb7e06.png)

すると、ログイン画面に出るので、
続いて、自分のFacebookアカウントでログインボタンをクリックし、ログインします。
![スクリーンショット 2019-12-03 16.54.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/27f451cb-ccc1-90e3-2568-cb5cc016931b.png)
ログインすると、以上のように、`Hello 自分の名前`と表示されます。
続いて、`Logout`をクリックし、ログアウトできるかの確認も行います。

![スクリーンショット 2019-12-03 16.54.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374924/111f3d14-d1ce-0c2e-5639-904b78ceb46c.png)

無事、最初のログイン画面に移動できたら、ログアウトの完了です。
お疲れ様でした。



##おまけ
認証された人しか見れないようにするには、app.jsに以下のように書き込みます。

```JS:app.js
//認証者を確かめる関数
function authenticatedUser(req, res, next) {
//認証されている人は次の処理が実行される。
  if (req.isAuthenticated()) { return next(); }
//認証されてない人は`/login`にリダイレクトされる。
  res.redirect('/login');
}
```
`/users`を認証者だけが見えるように設定。

```JS:app.js
app.use('/users', authenticatedUser, usersRouter);
```