AWS Mobile Hub (Cognito) + Amplify.js + Nuxt.jsでユーザ認証（Email+Password）をセットアップする

Cognitoの認証機能を簡単にsetupしつつ、webクライアント（js）からユーザ登録、ログイン、ログアウト、パス忘れに対応するお試し実装。
この実装フローでは**CognitoのWebコンソールを直接触らない。**

# 前提

* 2018.11.14現在
* yarn `1.10.1` https://yarnpkg.com/ja/
* 要AWSアカウント
* 用語ざっくり
  - `Mobile Hub` モバイルアプリやwebサービス開発で使えるAWSサービスを簡易にセットアップ出来るサービス（Mobile Hub自体は無料、他サービスは別途）
  - `Cognito` ユーザ認証とAWSへのアクセスコントロールを行うサービス（月間アクティブ50,000まで無料）
  - `Amplify` AWSの新しいライブラリ、Cognito等を簡易に利用できるよう作られたもの。
      - 公式docではグローバルインストールしたamplifyから`amplify configure`をすることでMobile Hubのセットアップをしつつappプロジェクトの作成が出来るような記述があるが、今回は使わない。
      - この記事は`aws-amplify-1.1.10` `aws-amplify-vue-0.1.7`を使用。

### CognitoのUser Pool, ID Poolについて（読み飛ばしてok）

- [公式図解（一例）](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html)
- `User Pool`は認証プロバイダ。ユーザ登録やログインの提供をする
- `ID Pool(フェデレイテッドアイデンティティ)`は認証プロバイダを束ね、AWSリソースへのアクセス権限のコントロール等を行う。
- ２種類のソーシャルログイン連携方法
   - `User Pool`は一つの認証プロバイダであり、Facebook等他のソーシャルログイン系もそれぞれ認証プロバイダとして、`ID Pool`で統括することが出来る。（ID Poolでの連携）
   - ややこしいのが、`User Pool`内でも、Facebook等ソーシャルログイン系認証プロバイダを組み込む事ができる。(User Poolでの連携)
   - 上記２つは別物。
- ユーザ登録、ログイン、パス忘れ等のユーザ登録認証のみを利用する場合、`ID Pool`は使われない。（が、Analyticsのためか、Mobile Hubで勝手に作られてaws-exportsにID Pool IDがセットされるため、そこはかとなく利用されている）


# 構築フロー

1. AWS マネジメントコンソールでMobile HubからCognitoの設定をして`app-config.zip`をダウンロードする
2. サンプル用Nuxtプロジェクトをセットアップする
3. プロジェクトにAmplifyのコードを入れて確認する

# 1. AWS マネジメントコンソールでMobile HubからCognitoの設定をして`app-config.zip`をダウンロードする

1. [コンソール](https://console.aws.amazon.com/mobilehub/home)からMobile Hubの新しいプロジェクトを作る 
1. `Create a project`...プロジェクト名はこの例では`cognito-test`。**リージョンをここで変更できるのでチェックする**
1. `Select app platform`...プラットフォームは`Web`を選ぶ。今回はweb hostingは使わないのでチェックを外す
  ![iE4OJ.png](https://qiita-image-store.s3.amazonaws.com/0/195818/8c372caf-2596-8305-2e2a-8331829d9671.png)
1. `Set up your backend`、`Connect to your backend`は説明だけなのでそのまま進める。
1. 作成したプロジェクトが表示（下図）されるので、ページ下の方へ行き`User sign-in`を選択。
 ![70fJ6.png](https://qiita-image-store.s3.amazonaws.com/0/195818/fa8871ad-8e8c-1371-2c2e-8c00aa4b65c0.png)
1. 下図のように`User sign-in`のセットアップをする(Email or phone numberを選択すれば、必須項目でそれらをチェックする必要なし)
   ![hoge.png](https://qiita-image-store.s3.amazonaws.com/0/195818/5c5cba99-39b8-0712-6afb-d3d0da171013.png)
1. 上図で`Create user pool`後、プロジェクト（`cognito-test`）の`Apps > Backend features`の`Integrate`を選択
1. `Update your backend`画面（下図）が出るので、`Download Cloud Config`から設定をDL
 ![YMbSv.png](https://qiita-image-store.s3.amazonaws.com/0/195818/f522663d-6181-94fb-6387-a257a154e365.png)
1. `app-config.zip`がダウンロードされ、解答すると`aws-config.js`と`aws-exports.js`があるはず。configはaws-sdk-js（旧ライブラリ）の設定ファイル（多分）で、exportsはamplify-js（新ライブラリ）の設定ファイル。使うのはexportsの方。



# 2. サンプル用Nuxtプロジェクトをセットアップする

* Nuxtプロジェクト`cognito-test`を作る。Mobile Hubのプロジェクト名と同名である必要はない。

```sh
# あれこれsetupの情報を聞かれるがよしなに。
$ yarn create nuxt-app cognito-test
# プロジェクトディレクトリに移動
$ cd cognito-test
# 初期インストール（必要ないかも）
$ yarn
# 開発ローカルサーバ立ち上げ
$ yarn dev
```

* `http://localhost:3000`にアクセスしてNuxtのスタートアップ画面が表示されるのを確認


# 3. プロジェクトにAmplifyのコードを入れて確認する

## amplifyの追加インストール

```sh
# すでにyarn devしてたら一度Ctrl+Cで切って、必要なパッケージをインストール
$ yarn add aws-amplify aws-amplify-vue
# その後、再度ローカルサーバ起動
$ yarn dev
```

## 設定ファイルaws-exports.jsの配置

* ダウンロードしたファイルを適当に配置（たとえばプロジェクトroot）

```sh
$ cp ~/Download/aws-config/aws-exports.js ~path/to/cognito-test/
```

## AmplifyセットアップのNuxtプラグインを追加

```js:plugins/amplify.js
import Vue from 'vue'
import Amplify, * as AmplifyModules from 'aws-amplify'
import { AmplifyPlugin, components } from 'aws-amplify-vue'
import aws_exports from '../aws-exports'

Amplify.configure(aws_exports)

Vue.use(AmplifyPlugin, AmplifyModules)
Vue.component(components)
```

## Nuxtのconfigでプラグイン読み込み

```diff:nuxt.config.js
- plugins: [],
+ plugins: [{ src: '~/plugins/amplify.js', ssr: false }],
```

* amplifyを使うのは認証のためなので、認証先のページではSSRの必要が無い想定。


## index.vueトップページの書き換え

```vue:pages/index.vue
<template>
  <section class="container">
    <div>
      <logo/>
      <no-ssr>
        <amplify-authenticator :auth-config="authConfig"/>
        <amplify-sign-out/>
      </no-ssr>
    </div>
  </section>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  data() {
    return {
      authConfig: {
        // 通常のログイン
        signInConfig: {
          header: 'Sign in !!'
        },
        // 登録
        signUpConfig: {
          hideDefaults: true,
          signUpFields: [
            { label: 'Email', key: 'username', required: true, type: 'email', displayOrder: 0 },
            { label: 'Password', key: 'password', required: true, type: 'text', displayOrder: 1 }
          ]
        },
        // 登録confirm
        confirmSignUpConfig: {},
        // パス忘れ
        forgotPasswordConfig: {},
        // MFA
        confirmSignInConfig: {}
      }
    }
  }
}
</script>
```

## ブラウザで確認

![localhost_3000_.png](https://qiita-image-store.s3.amazonaws.com/0/195818/5b104c01-ac30-36a2-0f8f-d48aed6fd518.png)

* `http://localhost:3000`で上記の様なページが表示されれば成功
* Create accountでユーザ仮登録（そのページで今度は検証コードが入力出来るようになるので、メールの検証コードを入力して本登録完了）
* 本登録したらSign in出来るようになる
* Forgot passwordも、ユーザ登録と同じフロー
* フォームに表示される`Username`とはEmailのこと。（これはMobile HubでのUser sign-inでEmailでサインイン出来るように設定しているため）

# このあと（未検証）

## フォームのカスタマイズ

* 本例だとEmailがUsernameと表示されちゃうとか、検証コード入力が画面残しとかないと入力できないとか、フォームデザインのカスタマイズとかする場合は、用意された`<amplify-authenticator>`等のComponentを使わず、jsで開発する。
* https://aws-amplify.github.io/docs/js/authentication

## 検証コード記載メールにワンタイムtoken付きのURLを載せる

* https://ap-northeast-1.console.aws.amazon.com/cognito/users
* Cognito User Pool画面から、`[対象のプロジェクト] > 全般設定 > メッセージのカスタマイズ`　から変更する [公式doc](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-settings-email-verification-message-customization.html)

## 検証メールの送信元（From, ReplyTo）の変更 

* https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-settings-email-address-customization.html
* https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-settings-ses-authorization-to-send-email.html

## ソーシャルログインの追加 

* 上述の通り、User Poolで連携する方法とID Poolに連携する方法がある。
* User Pool
    * 各ソーシャルログインプロバイダでのCognito用の設定と、awsコンソールでソーシャルログインプロバイダの情報を設定
    * https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-identity-federation.html
* ID Poolの連携
    * 各ソーシャルプロバイダのSDKを使って自前で認証してそのトークンを設定したID Poolにわたす
    * https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/external-identity-providers.html
* ユーザに直接AWSリソースを触らせず、ユーザ登録・認証のみを使う様な場合、ID Poolには出来るだけ触れずに、User Poolでやった方が良いように思う。が、現在のところUser Poolはtwitter認証ないのでそれが必要なら、ID Poolで頑張る感じになるのかも。（か、FirebaseとかAuth0を使うとか）
* Amplifyでのソーシャルログイン実装は未検証。公式docでは[ID Poolを使う](https://aws-amplify.github.io/docs/js/authentication#federated-identities-social-sign-in)ぽく書いてあるが、実際は[User Poolを使ってる](https://github.com/aws-amplify/amplify-js/issues/565#issuecomment-428054840)？みたいな記述もある。
* とりあえずAWSのドキュメントに従えば、指定URLに飛ばせばUser Poolでの認証フローになるはずなので、twitterが必要なければ、User Poolのみで対応できそう。

## 認証情報の取り回し

```diff:index.vue
<script>
+ import { Auth } from 'aws-amplify'

export default {

  // ... 省略

+  mounted() {
+    Auth.currentAuthenticatedUser()
+      .then(user => {
+         console.log(user.username)
+         console.log(user.signInUserSession.accessToken.payload.sub)
+         console.log(user)
+       })
+      .catch(err => console.log(err))
+  }
}
</script>
```

* これでログインしてページリロードとかすると、認証したユーザ情報がconsole上に表示される
* `Auth.currentAuthenticatedUser()`や`Auth.currentSession()`を使うと取れる`accessToken.jwtToken`をAPIバックエンドサーバ等に投げて、検証すれば、自サーバ上でも認証の確認が出来る。
* refreshTokenによるrefreshはAmplifyが自動でやってくれる。（ただしGoogle/Facebook以外のソーシャルログイン・あとAmplifyを使わないソーシャルログインとかはやってくれなそう）

## Cognito User Poolのアプリクライアントが2つ作られる

* `projectname_userpoolapp_MOBILEHUB_00000 web`
* `projectname_userpoolapp_MOBILEHUB_00000`
* の様に2種類のアプリクライアントがUser Pool上に作られるが、これはクライアントシークレットの有り無しだと思われる。webで使う方はクライアントシークレットの生成をしていないものを使う。もう一つは使い所不明。バックエンドとかからアクセスする用？



# 所感

* amplify自体が発展途上な感じがするが、ソーシャルログインを考えなければ実装コストはFirebase Authenticationと変わらない。
* jsならvueよりもReactの方ができること多そう。
* 無料枠も申し分ないし、ソーシャルログインを考えなければいい感じ。
* Cognitoはやっぱり魔境でプロダクトレベルで使うのはまだまだ勉強が足りないと思った。