AWS
cognito
nuxt.js
aws-amplify

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

Nuxt.jsAmazon Cognitoでユーザー登録、ログインを実装しました。
mode:'spa'です。

リポジトリ: nuxt-aws-amplify-sample

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

Cognitoの準備

ユーザープール作成

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

スクリーンショット 2018-06-14 15.36.53.png

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

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

スクリーンショット 2018-06-14 15.38.45.png

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

IDプール作成

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

スクリーンショット 2018-06-14 15.44.26.png

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

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

追加ライブラリ

AWS Amplify
JavaScriptからAWSの機能を利用するためのライブラリ。
@nuxtjs/dotenv
Nuxtで.envを利用するためのライブラリ。

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

aws-amplifyの初期化

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

.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をしないと一瞬画面が表示されてしまいます。

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に値があれば最初から入力されている状態になるようにしました。

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)
}
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するとログイン判定が行えません。
参考記事ではCookieを利用しています。
そもそもログイン機能が必要な画面でSSRする必要があるのか、という疑問……。

IAM

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

それはともかく

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