Express + Passport + Cognito でサーバーサイドのユーザー認証を手軽に実現

※ 会社のテックブログの転載です
http://blog.futurestandard.jp/entry/2016/10/19/192449

# 概要
AWS には、Cognito User Pools というユーザー認証機能を簡単に実装できるマネージドサービスがあります。しかしながら、AWSから提供されているSDKは、基本的にクライアントサイドでの利用を想定しており、iOS、Android、JavaScript（ブラウザ版） しか提供されていません。今回、弊社で開発をしている SCORER という動画解析サービスの開発にあたり、Node.js を使ったサーバーサイドでの認証に Cognito User Pools を利用したかったため、Passport という認証用ミドルウェアを作成することで簡単に実装が行えるようにしました。

[passport-cognito](https://www.npmjs.com/package/passport-cognito)


# Cognito とは
Cognito はAWSの提供するユーザー認証基盤です。実は、Cognito には、Cognito Identity と Cognito User Pools の二つのサービスがあり、前者はFacebook、Google、Twitter といった外部もしくは自前のIDプロバイダを必要とするのに対して、後者はユーザー登録、多要素認証、パスワード変更といったユーザー認証に関わる一切のサービスを提供してくれるため、簡単に独自ユーザー認証基盤を作成することができます。当初、Cognito を使い始めたときに、この辺りの違いが理解できず苦労しました。

ただし、Cognito Identity と Cognito User Pools は連携をさせることもでき、この場合は、Cognito User Pools が外部IDプロバイダと同じ役割をすることになります。詳しくは、下記のリンクの Use case 17. あたりが詳しいです。

[amazon-cognito-identity-js](https://github.com/aws/amazon-cognito-identity-js)

参考：
[Amazon Cognito User Poolsを使って、webサイトにユーザ認証基盤を作る](http://qiita.com/horike37/items/1d522f66452d3abe1203)
[Amazon Cognito User Poolsの情報をサーバサイドで取得する]([http://qiita.com/fullkawa/items/19eddc5a97f2308e3029)

# Passport とは
Express という Node.js のフレームワークとともに利用される認証用のミドルウェアです。FacebookやGoogle等を使ったOAuth認証、ベーシック認証といった様々な認証方法をを統一的なAPIで利用できるため Express をで認証機能を利用する際は、事実上の標準となっています。今回は、 Cognito User Pools の認証ができる Passport のモジュールを作成して公開しました。

# 使い方

Express と Passport の使い方に関しては下記の記事が大変参考になりました。本稿の説明も、基本的にはこちらに書いてある Passport の実装方法に拠っています。
[express実践入門](https://gist.github.com/mitsuruog/fc48397a8e80f051a145)

また、Cognito User Pools の設定はすでになされているものとします。詳しい設定方法に関しては下記の記事が詳しいのでご参照ください。
[[新機能] Amazon Cognito に待望のユーザー認証基盤「User Pools」が追加されました！](http://dev.classmethod.jp/cloud/aws/cognito-user-pool)

なお一点だけ注意点があり、iOSと違って JavaScript から利用する場合は、User Pools の App の設定画面で「Generate client secret」のチェックを必ず外してから作成を行ってください。  

![20161019210305.png](https://qiita-image-store.s3.amazonaws.com/0/48137/74deef89-1914-215c-98e8-9ab899f3ac60.png)

##  インストール
npm のパッケージとして公開しているため、下記のコマンドを入力するだけでインストール可能です。必要に応じて --save などのオプションをつけてください。

```sh
$ npm install passport-cognito
```

## ブラウザ側の実装

ブラウザ側からは、フォームを入力してボタンを押した際に、POSTで username と password を送るように設定します。例えば、jQueryで値を送る場合は下記のようになります。

```javascript
$.ajax({
  type: 'POST',
  url: '/auth/cognito',
  data: { username: username, password: password }
})
```

## サーバーサイドの実装

### Passport と middlware の設定
まず、Passport モジュールの読み込みとセッション用の middleware を設定します。ここはほぼコピペでOKです。

```javascript:app.js
var passport = require('passport');

// passportモジュールをLoad
require('./passport')(app);

// session用のmiddlewaresを有効化
app.use(passport.initialize());
app.use(passport.session());
```

### Strategy の設定
次に、Cognito 認証の Passport モジュールを読み込み、コンストラクタから必要なオプションを与えてインスタンスを生成します。コンストラクタの2つ目の引数が、コールバック関数になっており、認証が成功した場合はここでトークンやユーザー情報が取得できますので、必要に応じて処理を書いてください。なお、この user オブジェクトにトークン情報などを格納しておくと、ルーティングの際に、req.user を参照することで情報を引き回すことができるようになります。


```javascript:config/passport/cognito.js
var CognitoStrategy = require('passport-cognito')
 
module.exports = new CognitoStrategy({
    userPoolId: 'ap-northeast-1_eSjqLfqKc',
    clientId: 'vtvg02tr21zmxvspyvawtv09b',
    region: 'ap-northeast-1'
  },
  function(accessToken, idToken, refreshToken, user, cb) {
    process.nextTick(function() {
      ...
      cb(null, user);
    });
  }
);
```

### Serialize/Deserialize の設定
セッションへの情報の格納と復元の設定を行います。なお、ここで上記のコールバック関数の引数で渡した user を session 情報に格納しているため、req.user で値を引き回すことができるようになります。ここもほぼコピペでOKです。


```javascript:config/passport.js
module.exports = function(){
  var passport = require('passport');

  // sessionにユーザー(のキー)情報を格納する処理
  passport.serializeUser(function(user, done) {
    done(null, user);
  });

  // sessionからユーザー情報を復元する処理
  passport.deserializeUser(function(obj, done) {
    done(null, obj);
  });

  // 利用するstrategyを設定
  passport.use(require('./passport/cognito'));
}
```

### Routing の設定
下記のエンドポイントにPOSTでリクエストがあった際に、passport.authenticate() が呼ばれるようにします。なお、この際、req.body.username と req.body.password でブラウザから送られてきた値が取得できている必要があります。

```javascript
app.post('/auth/cognito',
  passport.authenticate('cognito', {
    successRedirect: '/',
    failureRedirect: '/login'
}));
```

# 最後に
上記の設定ができると、Cognito User Pools の認証は Passport モジュールが裏でいい感じに行ってくれ、AWSの他のサービスのアクセス等に必要なトークンの情報や User Pools に登録してあるユーザー情報が取得できるようになります。Express と Passport に多少は習熟する必要はありますが、上記のモジュールを使うと簡単にCognito User Pools の認証処理が実装できることをお分かりいただけたのではと思います。