aws-amplifyをnuxt.jsで使ってみた

# はじめに
[Nuxt.js](https://github.com/nuxt/nuxt.js)と[AWS Amplify](https://github.com/aws/aws-amplify)を組み合わせて認証をするだけのbareboneアプリケーションを作成した．忘れないうちに主にNuxt.jsに関して得られた知見を文字に起こす．
**筆者はNuxt.js初心者なので，間違いや改善点があればコメントをいただけると幸いです．**

今回作成したサンプルプロジェクトは[こちら](https://github.com/moly333/aws-amplfy-on-nuxt.js)
実装の参考にした[aws-amplify-vue](https://github.com/aws-samples/aws-amplify-vue)はVue.js使いなら必見である．

# 注意
Nuxt.jsのチュートリアルとAWS Amplifyのreadmeを読んだことがあるくらいの知識は必要なので，未読の方は読んでおいてください．(あとAWS入門ではないので，Cognito UserPoolとかの設定はよしなにやっておいてください)

またこの記事のNuxt.jsはSSR(サーバーサイドレンダリング)モードで開発，実行しているとする．

# サンプルプロジェクトについて

## 内容
AWS Amplifyを使い，Cognitoでユーザーの登録/ログイン/ログアウト/パスワードリセット/メール認証を行う．

## ディレクトリ構造
特に重要なものを以下に示す．

```text
├── components
│   └── amplify
│       ├── AmplifyTheme.js 認証系コンポーネントのテーマ
│       ├── ConfirmSignUp.vue などの認証系コンポーネント
├── layouts
│   └── default.vue デフォルトレイアウト
├── middleware
│   ├── guest.js 認証しないことを確認する
│   └── need_auth.js 認証されていることを確認する
├── pages
│   ├── Auth
│   │   ├── ConfirmSignUp.vue などの認証系ページ
│   ├── index.vue
│   └── secret.vue 認証していないと見れないページ
├── plugins
│   ├── AuthStorePersist.js 認証系のVuexを永続化する
│   ├── amplify_init.js aws-amplifyの初期化
│   ├── amplify_mixin.js 認証系のViewModelを共通化
│   ├── aws-exports.js aws-amplifyの設定ファイル
│   └── vue-notification.js vue-notificationを有効にする
├── store
│   ├── AuthStore.js 認証系のVuex
├── utils
    └── tokens.js tokenをいろいろするやつ
```

# 知見
## Nuxt.jsのbootstrapなコードはpluginsへ
AWS Amplifyを使うためには初期化が必要になる．Nuxt.jsでは初期化処理のようなbootstrap的な動作をするコードをpluginとしてもたせる．
今回は，AWS Amplifyの初期化，認証系の状態(Vuex)をlocalstorageに永続化するコードなどをpluginとして実装した．また，ViewModelの共通化としてmixinを使う場合もpluginとしてもたせればすっきりする．

またpluginをクライアントのみで実行する場合には，nuxt.config.jsにてpluginのssrオプションをfalseにすればよい．

```javascript:nuxt.config.js
  plugins: [
    { src: '~plugins/amplify_init.js', ssr: false },
    { src: '~plugins/AuthStorePersist.js', ssr: false },
    { src: '~plugins/amplify_mixin.js', ssr: true },
    { src: '~plugins/vue-notification' }
```

## 状態の永続化(クライアント&レンダリングサーバー)
基本的にVueでは状態管理にVuexを使うが，Vuexをクライアント側で永続化させるには，vuex-persistedstateを使うと便利である．

```javascript:plugins/AuthStorePersist.js
import createPersistedState from 'vuex-persistedstate'
export default ({ store }) => {
  createPersistedState({
    key: 'AuthStoreData',
    paths: ['AuthStore']
  })(store)
}
```

クライアント側はこれで済むのだが，レンダリングサーバーからはクライアントのlocalstorageを参照できない．従って，別にCookieを使用するなどの工夫が必要になる．
今回はSSR時のアクセス制御をmiddlewareで行っているために，ユーザーの認証状況をサーバーが把握する必要があったのでCookieに格納した．

```javascript:store/AuthStore.js
import Cookie from 'js-cookie'
...
export const mutations = {
  SET_USER (state, user) {
    state.user = user
    let token = getTokenFromUser(user)
    Cookie.set('tokens', token)
  },
...
```
このようにAWS Amplifyであつかうユーザー情報からトークンだけを抜き出し，これをCookieに入れた．

ちなみにAWS Amplifyは自らの情報をlocalstorageに格納しているので，クライアント側では永続化を気にせずただ呼び出すだけで良い．(素敵♥)

## middlewareの挙動
[middleware](https://ja.nuxtjs.org/guide/routing#%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2)とはページがレンダリングされる前に実行される処理を定義することができるレイヤーである．
middlewareの実行者はURLを直打ちした時や，初めてサイトに訪れた時などはサーバーで，それ以外(例えばボタンを押して遷移など)はクライアント側になるようである．
従って，以下のように処理を分岐させる必要がある．

```javascript:middleware/need_auth.js
...
export default async function ({ redirect, req }) {
  if (process.server) {
    //サーバー側の処理
  } else {
    //クライアント側の処理
  }
...
```
**認証をこのレイヤーに持たせると実装がスッキリするが他のNuxt使いはどうしているのだろうか？**

## SSRを一時的に無効にするコンポーネント
SSRをどうしても無効化したい場合は`<no-ssr>`を使えば良い．
詳しくは[この記事](https://qiita.com/potato4d/items/04d3767aaa036f557950)を参照．

# さいごに
以上がサンプルを書いて得たNuxt.jsの知見である．
既にNuxt.jsを使い倒している人たちからすれば常識かもしれないが，これからNuxt.jsでやっていきたいひとの手助けになることを願う．