Nuxt.jsとaws-amplifyでログインするだけ

[Nuxt.js](https://ja.nuxtjs.org/)と[Amazon Cognito](https://aws.amazon.com/jp/cognito/)でユーザー登録、ログインを実装しました。
`mode:'spa'`です。

リポジトリ: [nuxt-aws-amplify-sample](https://github.com/fkymnbkz/nuxt-aws-amplify-sample)

実装にあたり以下の記事とリポジトリを**大いに**参考にしました。

- [aws-amplifyをnuxt.jsで使ってみた](https://qiita.com/morimolymoly/items/0916a8122edbe8951a19)
- [aws-amplify-vue](https://github.com/aws-samples/aws-amplify-vue)
- [aws-amplify-react](https://github.com/aws/aws-amplify/tree/master/packages/aws-amplify-react)

# Cognitoの準備

## ユーザープール作成

基本的にはデフォルトのままです。１ヶ所だけ変えています。
「属性」の「エンドユーザーをどのようにサインインさせますか」を「Eメールアドレスおよび電話番号」「Eメールアドレスを許可」に変更します。
これにより、メールアドレスでログインできるようになります。

![スクリーンショット 2018-06-14 15.36.53.png](https://qiita-image-store.s3.amazonaws.com/0/221538/904804c5-b638-6c36-2186-42fac3c99632.png)

ユーザープールを作成すると、「プールID」が発行されます。

「アプリクライアント」を開きます。「クライアントシークレットを作成」のチェックを外し、作成してください。

![スクリーンショット 2018-06-14 15.38.45.png](https://qiita-image-store.s3.amazonaws.com/0/221538/ede24e68-9650-c685-1592-510fec869e50.png)

作成すると、「アプリクライアントID」が発行されます。

## IDプール作成

「認証されていないIDに対してアクセスを有効にする」にチェックを入れてください。
認証プロバイダーで「Cognito」を選び、「ユーザープールID」と「アプリクライアントID」に先ほど発行されたIDを登録します。

![スクリーンショット 2018-06-14 15.44.26.png](https://qiita-image-store.s3.amazonaws.com/0/221538/d2bacaa5-04bc-64bb-a320-d6e746eb38c6.png)

「IDプールの編集」画面に行くと、「IDプールのID（ややこしい）」がわかります。

これでCognitoの準備は終わりです。

# 追加ライブラリ

[AWS Amplify](https://aws.github.io/aws-amplify/)
JavaScriptからAWSの機能を利用するためのライブラリ。
[@nuxtjs/dotenv](https://github.com/nuxt-community/dotenv-module)
Nuxtで.envを利用するためのライブラリ。

```text
$ yarn add aws-amplify
$ yarn add @nuxtjs/dotenv
```

# aws-amplifyの初期化

初期化はpluginで行なっています。
.envファイルはコミットしていないので、ルートディレクトリに準備してください。

```text:.env
AWS_AMPLIFY_AUTH_REGION=リージョン
AWS_AMPLIFY_AUTH_USER_POOL_ID=ユーザープールID
AWS_AMPLIFY_AUTH_USER_POOL_WEB_CLIENT_ID=アプリクライアントID
AWS_AMPLIFY_AUTH_IDENTITY_POOL_ID=IDプールのID
```

たぶんaws-exportsを使う方がいいのでしょうが、フォーマットがわからなかったもので……。

# 画面

ログインや登録処理は各vueファイルに記載しています。

- index.vue: トップページ
- secret.vue: ログイン時にしか見られないページ
- signin.vue: ログインフォーム
- signup.vue: 新規登録フォーム
- confirm.vue: 認証フォーム

# ログイン判定

参考記事の知見に従い、middlewareに実装しました。未ログインの場合、ログインフォームに遷移します。
async/awaitをしないと一瞬画面が表示されてしまいます。

```js:amplify-auth.js
import { Auth } from 'aws-amplify'

export default async ({ redirect }) => {
  let signed_in = false
  await Auth.currentUserInfo()
    .then(data => signed_in = Boolean(data))
    .catch(err => console.log(err))
    .then(() => signed_in || redirect('/signin'))
}
```

aws-amplify-vueでは、`Auth.currentUserInfo()`ではなく、`Auth.currentAuthenticatedUser()`をしてから`Auth.currentCredentials()`しています。
どちらの方がよいのでしょうか？
というか単純にログイン状態を見るようなことはできないのね。

# 登録後の認証

Cognitoに新規登録をすると、メールアドレスに認証コードが送られます。認証に必要なのはこのコードとusername（今回はメールアドレス）です。
何度も入力するのは手間なので、新規登録時にメールアドレスをstoreに保管し、認証フォームではstoreに値があれば最初から入力されている状態になるようにしました。

```js:signup.vue
signUp () {
  Auth.signUp(this.email, this.password)
    .then(data => {
      this.$store.dispatch('amplify/setUsername', this.email)
      this.$router.push('/confirm')
    })
    .catch(err => this.errors = err)
}
```
```js:confirm.vue
data () {
  return {
    email: this.$store.getters['amplify/username'],
    code: '',
    errors: ''
  }
},
```

aws-amplify-reactでは、storeにusernameがあればformにhiddenを追加し、なければ入力欄を表示するような作りになっています。

# その他

プロダクションレベルにするためには、まだまだ考慮しなければいけないことが多そう。

## エラー処理

入力値のバリデーションはaws-amplifyでやってくれます。
バリデーションエラーを含めたエラーメッセージは日本語化されないため、独自に設定する必要がありそうです。

## パスワード変更必須

Cognito側でユーザーを作成すると仮パスワードが発行され、パスワード変更必須状態になります。
ログイン時にユーザーの状態を確認し、パスワード変更画面に遷移させる必要があるでしょう。

## SSR

aws-amplifyはlocalStorageを使っているため、SSRするとログイン判定が行えません。
[参考記事](https://qiita.com/morimolymoly/items/0916a8122edbe8951a19)ではCookieを利用しています。
そもそもログイン機能が必要な画面でSSRする必要があるのか、という疑問……。

## IAM

Cognitoが行うのはユーザーの認証だけで、リソースへのアクセス許可はIAMで行います。
IDプールで認証されたユーザーに与えられるロールが決められるので、適切に設定する必要があります。
（この辺りがややこしくて現状は「あーそーゆことね完全に理解した」状態）

# それはともかく

慣れるとPHPとMySQLでゴニョゴニョするよりはるかに楽です。
もうユーザー関連は全部Cognito任せになってほしい。