Python3のLambdaからCognito UserPoolsを利用する

CognitoのUserPoolsでユーザー認証をするときに、公式でNode.jsの例がありますが、Pythonで実行する例がないので、[warrant](https://github.com/capless/warrant)というライブラリを使った例を説明します。


## 前提
以下を前提で説明を進めます。

* Cognitoのユーザープールを作成済み
* Serverless Frameworkがインストール済み
* Dockerがインストール済み

Dockerは、Serverless Frameworkのプラグインの `serverless-python-requirements` を使用する際に必要となります。


## warrantのインストール
warrantはpipでインストールできます。

```bash:warrantのインストール
$ pip install warrant
```


各種ツールの使用したバージョンは、それぞれ以下になります。

| Python関連ツール | バージョン |
|:-------|:--------|
| Python | 3.6.2 |
| pip | 9.0.1 |
| warrant | 0.4.0 |

| Serverless関連ツール | バージョン |
|:-------|:--------|
| Serverless Framework| 1.23.0 |
| serverless-python-requirements | 3.0.1 |


## 事前準備
Lambdaでwarrantを使用するために、以下が必要となります。

* warrant自体も含めてアップロードが必要
* warrantが依存するパッケージでプリコンパイルが必要なものがあるため、Amazon Linuxでのコンパイルが必要

これらを簡易的に実行するため、Serverless Frameworkのプラグイン `serverless-python-requirements` を使用します。
このプラグインがdeploy時に、warrant含めてアップロード＆Dockerを使用してAmazon Linuxでのコンパイルを実行してくれます。

プラグインは、npmでインストールします。

```bash:serverless-python-requirementsプラグインのインストール
$ npm install --save serverless-python-requirements
```

次に、requirements.txtという名前でファイルを作成し、 `warrant` とだけ書いてください。
このファイルを `serverless-python-requirements` が読み、deploy時にwarrantを含めてくれるようになります。

```txt:requirements.txt
warrant
```


## serverless.ymlの作成
Serverless Frameworkを使うので、serverless.ymlを作成します。
最小限の情報だけ書くと以下のようになります。

```yaml:serverless.yml
# サービス名
service: warrant-sample

# aws用の設定
provider:
  name: aws
  runtime: python3.6
  stage: dev
  region: ap-northeast-1

# プラグインの設定
plugins:
  - serverless-python-requirements

custom:
  # serverless-python-requirements用の設定
  pythonRequirements:
    dockerizePip: true

functions:
  auth:
    handler: handler.auth
```

pluginsとcustomのところで `serverless-python-requirements` に関する設定をおこなっています。
`dockerizePip: true` がDockerを使用したAmazon Linuxでのコンパイルを実施することを意味します。


## Cognito認証処理
Lambda内で、warrantを使用したCognito認証処理が以下となります。


```python:handler.py
from warrant import Cognito


def auth(event, context):
    # event内から必要なパラメータ取得
    cognito_pool_id = event['cognito_pool_id']
    cognito_client_id = event['cognito_client_id']
    username = event['username']
    password = event['password']

    # Cognitoアクセス用オブジェクトの作成
    cognito = Cognito(
        cognito_pool_id,
        cognito_client_id,
        user_pool_region='ap-northeast-1',
        username=username)

    # 認証処理の実行
    cognito.authenticate(password=password)

    return cognito.id_token
```

Cognitoオブジェクト（warrantのクラス）を作成して、authenticateメソッドでCognitoの認証を実行しています。
認証に成功すると、Cognitoオブジェクト内の `id_token` に発行されたトークンが格納されます。


## デプロイ
準備が完了したので、デプロイを実行してください。

```bash:deploy
$ sls deploy
```

## テスト
デプロイしたLambdaのページでテストを実行してみてください。
テストデータは以下のようなJSONデータとなります。各種IDなどは各自の値を設定してください。

```json:example.json
{
  "cognito_pool_id": "<cognitoのプールID>",
  "cognito_client_id": "<cognitoのアプリクライアントID>",
  "username": "<ユーザープールのユーザー名>",
  "password": "<ユーザープールのユーザーのパスワード>"
}
```

結果として、tokenが返ってくると思います。
このように、Cognito UserPoolsをPythonから使用することができます。

## 以上
warrantを使うことで、Pythonでも簡単にCognito UserPoolsを使用できるので、試してみてください。

今回使用したソースコードはgithubに置いているので、ディレクトリ構成の確認などしたい場合は、そちらもご確認ください。
[https://github.com/ombran/warrant-sample](https://github.com/ombran/warrant-sample)