javascriptでCognitoユーザプール認証

javascriptからCognitoのユーザプールで認証する方法を確認しました。
Amazon API GatewayのオーソライザーとしてCognito認証を指定することができますが、API Gatewayにアクセスする際にはCognito認証で取得したIDトークンが必要となります。
今回はIDトークンの取得とAPIへの与え方の確認になります。(qiita初投稿です）

###0. 今回利用するサービスやモジュールたち
1. Cognito (ユーザプール)
2. node.js (javascript runtime)
3. amazon-cognito-identity-js (Cognito javascript SDK)
4. Axios (XHR library)

###1. Cognito (ユーザプール) の設定  
今回はユーザ・パスワードで認証させるだけなので、シンプルな構成でユーザプールを作成します。
ユーザプールIDとアプリクライアントIDはCognito認証時のパラメータとして必要となります。

1. ユーザプールを作成していきます。
<img width="1065" alt="userpool_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/280717/7bfed20b-86bf-d4c2-7ae0-95743c159c54.png">
<br>

2. アプリクライアントの追加は実行しておきます。
<img width="765" alt="userpool_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/280717/51340249-7de4-8543-cd6b-3e30bd099424.png"><br>
<img width="761" alt="userpool_3.png" src="https://qiita-image-store.s3.amazonaws.com/0/280717/61663522-502f-b67a-4453-2d396448e572.png">
<br>
3. アプリクライアントの追加が完了したらユーザプールを作成します。
<img width="772" alt="userpool_4.png" src="https://qiita-image-store.s3.amazonaws.com/0/280717/7bf0114d-d26e-8123-504e-7053cc80f512.png">

完了したらCognito認証で利用するユーザも作成しておきましょう。
<img width="609" alt="userpool_5.png" src="https://qiita-image-store.s3.amazonaws.com/0/280717/da075bbf-c28f-d633-8d56-e4bf3affb8d6.png">

またユーザプールIDとアプリクライアントIDも確認しておきます。
(それぞれユーザプールの全般設定、アプリの統合＞アプリクライアントの設定で確認します。)

###2. Node.jsの設定
詳細は検索するなり参考サイトを確認ください。
MacOS(nodebrew)の場合は以下となります。 
 
```
vi ~/.bash_profile
   export PATH=$HOME/.nodebrew/current/bin:$PATH #この行を追記

brew install nodebrew
nodebrew install v8.10.0
nodebrew use v8.10.0
```
  
node.jsはDocker公式イメージが配布されているので、Dockerコンテナを使う手もありと思います。

```
docker pull node
docker run -it --name node node /bin/bash
...Dockerコンテナ内でセットアップ...。
```

以下のjavascriptライブラリを使うのでnode実行環境にインストールしておきます。(global installの方が良い？その場合はnpm install -g）
npm install --save amazon-cognito-identity-js
npm install --save axios
npm install --save node-fetch
以下のjavascriptを実行する際に必要となります。


###3. Cognito認証するスクリプト実装
ユーザプール情報や認証データ情報を格納したオブジェクトをもとにCognitoUserオブジェクトを作成。
authenticateUser関数を実行し認証に成功すると、コールバック(onSuccess)に指定した関数が呼ばれます。(認証処理に失敗するとonFailureがコールされます)
コールバック関数内でAPIコールを行う処理を実装していますが、もっと良い方法があるかもしれません。
初回ログイン時はパスワード変更が必要となりますので(newPasswordRequiredがコールバックされる）、暫定で同一パスワードを設定します。
またnode.jsからスクリプトを直接実行する場合、ライブラリ群はCommonJS Modulesの形式でインポート(require)する必要があるようです。(SPAフレームワークの場合はES Modules形式も良いもよう)

```testauth.js
global.fetch = require('node-fetch')
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const axios = require('axios');
const config = require('./config')

login = function () {
	let poolData = {
		UserPoolId: config.UserPoolId,
		ClientId: config.ClientId
	};
	let userData = {
		Username: config.Username,
		Pool: new AmazonCognitoIdentity.CognitoUserPool(poolData)
	};
	let cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

	let authenticationData = {
		Username: config.Username,
		Password: config.Password
	};
	let authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);

	cognitoUser.authenticateUser(authenticationDetails, {
		onSuccess: function (result) {
			let idToken = result.getIdToken().getJwtToken(); //Cognito認証で取得したIDトークン。 API gatewayへのアクセスに必要となる。
			get(idToken);  //IDトークンを指定してHTTPリクエスト(GET)を実行！
		},
        onFailure: function (err) {
			console.log(err);
		},
		newPasswordRequired: function (userAttributes, requiredAttributes) {
			cognitoUser.completeNewPasswordChallenge(config.Password, {}, this);
		}
	});
}
```  

続いてHTTPアクセス用の関数の処理を追加します。
XHRライブラリはAxiosを使いました。
axios.get関数にて指定したURLへGetリクエストを送信できます。
Cognito認証を有効にしたAmazon API Gatewayにアクセスする場合、AuthorizationヘッダーにIDトークンを指定します。

```testauth.js
get = function (idToken) {
    let headers = { headers: { 'Authorization': idToken } };

    axios.get(config.apiGatewayUrl, headers).then(
		function (res) {
			console.log(res.data);
		}
 	).catch(
		function (err) {
			console.log(err);
		}
	);
};

login(); //おっと、こいつがないとAPIコールされんかったです。
```

ユーザプール情報や認証情報は外部スクリプトに記載しておき、処理時にrequireでインポートします。

```config.js
module.exports =  {
    apiGatewayUrl: 'https://xxxxx.xxxx.amazonaws.com/yourapi',
    UserPoolId: 'CognitoユーザプールID',
    ClientId: 'CognitoユーザプールのクライアントアプリID',
    Username: 'Cognito認証を利用するユーザのID',
    Password: 'Cognito認証を利用するユーザのパスワード'
}
```

<br>

上記を用意したら、コマンドラインからのスクリプト実行にてAPIコールが可能です。

```
node testauth.js
```

APIコールのレスポンスがコンソールに出力されますが、スクリプトやAPI側に必要に応じて何らかの処理を実装します。  


***
##まとめ
javascriptからCognitoのユーザプールで認証し、その際に取得したIDトークンをHTTPヘッダーに含めAPIコールを行う処理を試してみました。次はAmazon API Gatewayの実装方法を確認していきたいと思います。
また誤り漏れや改善点があれば適宜修正していきたいと思います。(例によって説明不足のところもあるので・・・。) 

***
###参考サイト
[Amazon-Web-Servicesを使ったサーバーレスアプリケーション開発ガイド(書籍)](https://www.amazon.co.jp/Amazon-Web-Servicesを使ったサーバーレスアプリケーション開発ガイド-西谷-圭介/dp/4839964564/ref=sr_1_20?ie=UTF8&qid=1534176574&sr=8-20&keywords=aws)
[DockerHub(node)](https://hub.docker.com/_/node/)
[nodebrew](https://github.com/hokaccha/nodebrew)
[fetch is not defined #403](https://github.com/aws-amplifyamplify-js/issues/403)
[amazon-cognito-identity-js](https://www.npmjs.com/package/amazon-cognito-identity-js)
[axios](https://github.com/axios/axios)