AWS Cognito UserPools を Cordova (Javascript) で使う最初の一歩

## 概要
AWS の アカウント管理サービス Cognito UserPools が東京リージョンでも使えるようになりました。
Cognito では、iOS/Android/Javascript の SDK が用意されています。
今回は Javascript 版を使って、Cordova から利用するための雛形を作成します。

## Cognito

![001.png](https://qiita-image-store.s3.amazonaws.com/0/69410/4ac2f6fc-e1aa-0a07-b1b4-c62336f224d6.png)

### Cognito User Pools
まず、User Pool を作成します。
![001_2.png](https://qiita-image-store.s3.amazonaws.com/0/69410/f5e78a90-d010-191e-9b9c-297f5fd7981e.png)

「Create a User Pool」をクリック。
(↓画像は、すでに１個存在している状態です。気にしない)
![003.png](https://qiita-image-store.s3.amazonaws.com/0/69410/b028c349-97d2-5882-0c21-b35471ce4757.png)

「Pool Name」を設定します。今回は「QiitaPool」という名前にします。
ここでは、「Step through setting」で細かく設定しながら作成しましょう。
![004.png](https://qiita-image-store.s3.amazonaws.com/0/69410/270174fd-25c7-c98e-8e95-9b9c6309f7a3.png)

ユーザの追加属性を選択します。
「email」はデフォルトで「On」なのでそのままで。
今回は「locale」も足してみました。(将来は多言語対応したいので。)
![005.png](https://qiita-image-store.s3.amazonaws.com/0/69410/a3d77845-5f0b-486d-5505-d7fb47b10d88.png)

パスワードのポリシーを設定します。お好みで。
![006.png](https://qiita-image-store.s3.amazonaws.com/0/69410/d9e9c73a-8e11-5c16-beb6-e055779c7149.png)

デフォルトでは、Email でベリファイするようになっています。
東京リージョンですが、Email のテンプレートに日本語は使用できないようです。
![024.png](https://qiita-image-store.s3.amazonaws.com/0/69410/b3327a92-be5c-ab37-6a49-72731d6f554b.png)

スマフォの場合に、ログインユーザを記憶してくれるのかな？
後から設定できるようなので、ここはデフォルトの「No」で先に進む。
![007.png](https://qiita-image-store.s3.amazonaws.com/0/69410/4fb68e2f-7960-a856-7cac-fc0f95b90af3.png)

次が重要

「アプリケーション」の追加。(少なくとも一つは設定しておかないと何もできない。)

今回は「QiitaTestApp」という名前で作成します。
Javascript SDK を使う場合、「Generate client secret」は OFF にするように書いてあった。

https://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html
![023.png](https://qiita-image-store.s3.amazonaws.com/0/69410/1156a464-19bd-8376-843e-33622f26d1b1.png)

「Create app」ボタンを、忘れずに押す。
![010.png](https://qiita-image-store.s3.amazonaws.com/0/69410/86acea05-e945-b7b0-02a2-2c6bde76e854.png)

ユーザ生成/認証の各フェーズで、Lambda をコールバックする機能があるようです。
後から、必要になったら設定しよう。先に進む。
![009.png](https://qiita-image-store.s3.amazonaws.com/0/69410/16ec224a-3652-0e6d-31a7-8eddce8a3702.png)

最後の確認。

この確認画面からの編集は、バグっているのか、うまく編集することができません。
そんなときは、最初に戻ってやり直したほうがいいです。

問題ないなら「Create pool」を押下。
![011.png](https://qiita-image-store.s3.amazonaws.com/0/69410/09f593fe-15d8-d480-f522-93035585fa27.png)



うまく作成されると、以下のようになります。
![012.png](https://qiita-image-store.s3.amazonaws.com/0/69410/c3669cc4-6511-17ec-d660-26f115358f22.png)

追加されました。
![013.png](https://qiita-image-store.s3.amazonaws.com/0/69410/46204abe-5f96-9d1a-da6e-d89e8929a543.png)

### ID確認
ここで取得したのは、UserPoolのID と AppのID。もう一回確認しておこう。
UserPoolのIDは、以下。 AppのIDは 鉛筆アイコンをクリックして、
![021.png](https://qiita-image-store.s3.amazonaws.com/0/69410/4179619e-f511-d35b-5feb-5c1325e60ac7.png)
以下に、AppのIDが書いてある。メモしておく。
![022.png](https://qiita-image-store.s3.amazonaws.com/0/69410/bb0602ae-77f8-f50a-0cdc-1ef8bc1a11bb.png)


### Cognito Identity Pools

次は、Identity Pool を作成する。

Identity Pool と User Pool が別々に存在しているのがよくわからないが、
Identity Pool と User Pool は 1対多 の関係にあるようだ。

![001_のコピー.png](https://qiita-image-store.s3.amazonaws.com/0/69410/d2c5c7b8-81dd-d956-d17c-c912488a6ec5.png)

「Create new Identity pool」をクリックして作成する。
(↓画像は、すでに２個存在している状態です。気にしない)
![002.png](https://qiita-image-store.s3.amazonaws.com/0/69410/4690b49c-1989-4cc5-e56d-79e9f2042b9b.png)

「QiitaIdentityPool」という名前で作成します。

「Unauthenticated Identities」は、ログイン前のアクセスに対する権限の付与だと思われる。
この需要はよくあるので、今回は ON にしておきます。
これらの権限は後から設定する。

「Authenticated providers」の設定。
ここで、先ほど作成した User Pool の ID と、App の ID を設定する。

他にも、Amazon, Facebook, Google, Twitter, OpenID, SAML などが Provider として設定できるようだ。

あぁ、やっと意味がわかった。UserPool は 認証プロバイダ で、IdentityPool は 認証クライアント の役割か。
そういえば、Cognitoは、複数プロバイダのアカウントを「名寄せ」する機能もあると、どこかで読んだ。

「Create Pool」を押下して、先に進む。
![016.png](https://qiita-image-store.s3.amazonaws.com/0/69410/d69553cc-b345-d48e-6922-26b0144a21c3.png)

認証前と認証後のユーザの、ロールを作成する。
「新しいIAMロールの作成」しかできないようだ。なぜか既存のロールは選択できない。

特に変更せずに「許可」を押下。先に進む。
![017.png](https://qiita-image-store.s3.amazonaws.com/0/69410/7e58089c-6e7f-19ea-783a-c7feb371f21b.png)

サンプルコードを生成してくれる画面が、ここで挟まれる。
SDK のダウンロードも出来るようになっている。

「Go to Dashboard」を押下すれば、完了。
![018.png](https://qiita-image-store.s3.amazonaws.com/0/69410/3a8e8ad2-0eb3-6029-6d3f-e6836132386d.png)

### ID確認
ここで取得したのは、IdentityPoolのID。もう一回確認しておこう。
![020.png](https://qiita-image-store.s3.amazonaws.com/0/69410/1d6caf69-2b52-9970-eb2e-39393426701c.png)

## セットアップ
Cognito を使うために必要なライブラリ を、以下を参考に集めてくる
https://docs.aws.amazon.com/cognito/latest/developerguide/setting-up-the-javascript-sdk.html

 - Amazon Cognito AWS SDK for JavaScript  
   https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/aws-cognito-sdk.min.js
 - Amazon Cognito Identity SDK for JavaScript  
   https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/amazon-cognito-identity.min.js
 - JavaScript BN library for BigInteger computations  
   http://www-cs-students.stanford.edu/~tjw/jsbn/
 - Stanford JavaScript Crypto Library
   https://github.com/bitwiseshiftleft/sjcl
   codecBytes を含めてビルドする必要があるらしい。手順は以下。

    ```
$ git clone https://github.com/bitwiseshiftleft/sjcl.git
$ cd sjcl
$ ./configure --with-all
$ make
```
 - Moment.js, a JavaScript library used for date manipulation
   http://momentjs.com/
 - AWS SDK for JavaScript in the Browser
   https://github.com/aws/aws-sdk-js/releases
   (または 直リンク https://sdk.amazonaws.com/js/aws-sdk-2.6.9.min.js )

全部で以下になる。これらは後で、まとめて、cordova に入れよう。

```
amazon-cognito-identity.min.js
aws-cognito-sdk.min.js
aws-sdk.min.js
moment.min.js
sjcl.min.js
jsbn.js
jsbn2.js
```

## Cordova

### セットアップ
https://cordova.apache.org/
![スクリーンショット 2016-08-11 22.25.45.png](https://qiita-image-store.s3.amazonaws.com/0/69410/b7958c75-bac9-fddd-f08c-aae6cfb726f1.png)

Get Started の手順どおり。
名前は cordova_cognito にした。

```
$ npm install -g cordova
$ cordova create cordova_cognito
$ cd cordova_cognito
$ cordova platform add browser
$ cordova run browser
```

### 雛形コード

 - ライブラリを www/js にコピー
 - index.html にフォームを追加
 - www/js/cognito.js に処理を追加

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8"/>
    <!--
    <meta http-equiv="Content-Security-Policy"
          content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
    -->
    <meta name="format-detection" content="telephone=no">
    <meta name="msapplication-tap-highlight" content="no">
    <meta name="viewport"
          content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css">
    <link rel="stylesheet" type="text/css" href="css/index.css">
    <title>Hello World</title>
</head>
<body>
    <div class="app">
        <h1>Apache Cordova</h1>
        <div id="deviceready" class="blink">
            <p class="event listening">Connecting to Device</p>
            <p class="event received">Device is Ready</p>
        </div>
    </div>

    <form>
        <h2>サインアップ</h2>
        <div id="message" class="alert" style="display:none;"></div>
        <label for="inputUsername" class="sr-only">ユーザー名</label>
        <input type="text" id="inputUsername" class="form-control" placeholder="User name" required autofocus></input>
        <label for="inputPassword" class="sr-only">パスワード</label>
        <input type="password" id="inputPassword" class="form-control" placeholder="Password" required></input>
        <label for="inputEmail" class="sr-only">メールアドレス</label>
        <input type="text" id="inputEmail" class="form-control" placeholder="EMail" required></input>
        <label for="inputLocale" class="sr-only">ロケール</label>
        <input type="text" id="inputLocale" class="form-control" placeholder="Locale" required></input>
        <input type="button" class="btn btn-lg btn-primary btn-bloc" id="user_add_btn" value="ユーザーを作成する"></input>
    </form>



    <script type="text/javascript" src="js/min/aws-sdk.min.js"></script>
    <script type="text/javascript" src="js/src/jsbn.js"></script>
    <script type="text/javascript" src="js/src/jsbn2.js"></script>
    <script type="text/javascript" src="js/min/sjcl.min.js"></script>
    <script type="text/javascript" src="js/min/moment.min.js"></script>
    <script type="text/javascript" src="js/min/aws-cognito-sdk.min.js"></script>
    <script type="text/javascript" src="js/min/amazon-cognito-identity.min.js"></script>
    <script type="text/javascript" src="js/min/jquery-2.2.4.min.js"></script>
    <script type="text/javascript" src="js/min/bootstrap.min.js"></script>
    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
    <script type="text/javascript" src="js/cognito.js"></script>

</body>
</html>
```



```js:cognito.js
var region = 'ap-northeast-1'; // 東京リージョン

var IdentityPoolId = {
  AWS: 'ap-northeast-1:4a0a8023-7770-499a-a92e-e8cd3d788871', // <YOUR_IDENTITY_POOL_ID>
  AWSCognito: 'ap-northeast-1_uSVyq55eG' // <YOUR_USER_POOL_ID>
};

var ClientId = '6elsp8qa9ecfbj8la64vrkr8ts'; // アプリID

///////////////////

var idp = ['cognito-idp', region, 'amazonaws', 'com'].join(".");
var endpoint = [idp, IdentityPoolId.AWSCognito].join("/");

// Initialize the Amazon Cognito credentials provider
AWS.config.region = region; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: IdentityPoolId.AWS
});

// Initialize the Amazon Cognito credentials provider
AWSCognito.config.region = region; // Region
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: IdentityPoolId.AWSCognito
});

var data = {
  UserPoolId: IdentityPoolId.AWSCognito,
  ClientId: ClientId
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(data);

var cognitoUser;

function message(div, message_text, message_class) {
  div.text(message_text);
  div.addClass(message_class);
  div.show();
  setTimeout(function () {
    div.fadeOut();
    div.removeClass(message_class);
  }, 5000);
}

// Use case 1. Registering a user with the application.
$('#user_add_btn').click(function () {
  var username = $("#inputUsername").val();
  var password = $("#inputPassword").val();

  var email = $("#inputEmail").val();
  var locale = $("#inputLocale").val();

  var attributeList = [];
  attributeList.push({Name: "email", Value: email});
  attributeList.push({Name: "locale", Value: locale});

  if (!username || !password) {
    return false;
  }

  userPool.signUp(username, password, attributeList, null, function (err, result) {
    if (err) {
      console.log(err);
      message($('#message'), err, "alert-danger");
    } else {
      cognitoUser = result.user;
      console.log('user name is ' + cognitoUser.getUsername());
      var message_text = cognitoUser.getUsername() + "が作成されました";
      message($('#message'), message_text, "alert-success");
    }
  });
});
```

### 使い方

 - 「サインアップ」でユーザ登録すると、EMail宛に Verification コードが送られてきます。
 - 「コンファーム」で Verification コードを入力して、コンファームします。
 - コンファーム後は、「ログイン」でログインが出来るようになります。


![スクリーンショット 2016-08-12 0.26.44.png](https://qiita-image-store.s3.amazonaws.com/0/69410/7baba427-7c1c-85e3-3d40-e2ecd5407396.png)

## ソースコード
 - 雛形ソースコード一式を GitHub にアップロードしておきます。
 - Pool ID は晒してますので、雛形ソースコードのままで ユーザ登録/ログインが可能です。
 - ロールに権限をつけていないので、実際には何も出来ないです。

#### 使い方
```
$ git clone https://github.com/exabugs/cordova_cognito.git
$ cd cordova_cognito
$ cordova run browser
```
#### 修正箇所
www/js/cognito.js の ID 部分を、あなたの設定に変更して下さい。

```js:cognito.js
var IdentityPoolId = {
  AWS: 'ap-northeast-1:4a0a8023-7770-499a-a92e-e8cd3d788871', // <YOUR_IDENTITY_POOL_ID>
  AWSCognito: 'ap-northeast-1_uSVyq55eG' // <YOUR_USER_POOL_ID>
};

var ClientId = '6elsp8qa9ecfbj8la64vrkr8ts'; // アプリID
```

## おわりに
 - 実装が面倒なアカウント管理/認証 がサービスとしてあるので、とても便利です。
 - Facebook/Google/Twitter 等への認証も、今後試してみます。