AWS AmplifyとVue.jsで認証画面

AWS Amplify, IT記事

AWS Amplifyの認証まわり、ログイン画面からやってみる。amplify cliはバージョンアップで結構変わるようなので注意です。

(2020/07/20 追記 : npm installするライブラリが変わり、コード周りが変わったので、こちらに新しく記事を書きました。)

まずVueプロジェクトを作り、移動する。

vue create amplify_auth_test

? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, CSS Pre-processors, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? Yes

cd .\amplify_auth_test\

vuexを入れるのを忘れていた。追加で入れる

vue add vuex

または vue uiで入れる。uiでプロジェクトを認識させて、pluginからvuexのプラグイン入れてコミット。

vue ui

vue プロジェクトの用意はできたので、次はamplifyの設定を行う。

 amplify init

? Enter a name for the project amplify_auth_test
? Enter a name for the environment authdev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  npm.cmd run-script build
? Start Command: npm.cmd run-script serve
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use (Use arrow keys)
> default
? Please choose the profile you want to use default

エディターやjs環境で使うということや、vueをつかうことや、出力先がdistフォルダであることや、aws cliの認証情報(profile)でdefaultの方を使うことを設定している。最後の質問は、aws cliで複数の認証情報を作っていたので選んでいる。

次にauthを入れる。

amplify add auth

 Do you want to use the default authentication and security configuration? Default configuration
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? Yes, I want to make some additional changes.
 Warning: you will not be able to edit these selections.
 What attributes are required for signing up? Email
 Do you want to enable any of the following capabilities?

最初の質問は、サインインのパターンで、ユーザー名&パスワード方法か、open api使うか聞かれる。open apiはまだよくわからないので、デフォルトの方。

次に、サインインにユーザー名かemailか聞かれる。emailにした。

次に、サインアップ時に必要な属性(メールアドレス、住所、etc)の設定ができる。yesを選んで、emailだけにした。

最後のは、認証時にlambdaを呼び出せるらしく、lambdaのテンプレートを選べるらしい。どれも選ばずエンター。

pushしてawsの方に反映する。

amplify push

AWS cognitoにアクセスして、ユーザプールを確認してみよう。 新しく作られている。 設定がどうなったか見たほうがいい。

Vue画面

vueのコードを書いていく。Vueをつかったサンプルプロジェクトとして、 aws-amplify-vue が公開されているので参考にする。

npmでaws amplifyを入れておく。

npm i aws-amplify aws-amplify-vue --save

AWS Amplifyコンポーネントのインポート

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

Vue.config.productionTip = false;

import Amplify from "aws-amplify";
import awsconfig from "./aws-exports";
import { components } from "aws-amplify-vue";

Amplify.configure(awsconfig);

new Vue({
  router,
  store,
  render: h => h(App),
  components: { ...components }
}).$mount("#app");

routerコンポーネントで、Vue.use(AmplifyPlugin, AmplifyModules)をしたいので、ここではawsconfigと、componentsを読みこむだけにしている。

次にrouter。amplify vueの読み込みの続きや、ページ遷移するごとにログインしているか (セッション) チェックしたり、vuexでユーザー名を保持したり、Profile画面を増やしたり、/loginページを作ったりしている。

router/index.js

import Vue from "vue";
import VueRouter from "vue-router";

import Home from "../views/Home.vue";
import Profile from "@/views/Profile.vue";
import AmplifyStore from "@/store/index";
import AmplifyAuth from "@/views/AmplifyAuth";

import * as AmplifyModules from "aws-amplify";
import { AmplifyPlugin, components, AmplifyEventBus } from "aws-amplify-vue";

Vue.use(AmplifyPlugin, AmplifyModules);
Vue.use(VueRouter);

let user;

getUser().then((user, error) => {
  if (user) {
    router.push({ path: "/" },()=>{});
  }
  if (error) {
    console.error(error);
  }
});

AmplifyEventBus.$on("authState", async state => {
  if (state === "signedOut") {
    user = null;
    AmplifyStore.commit("setUser", null);
    router.push({ path: "/login" });
  } else if (state === "signedIn") {
    user = await getUser();
    router.push({ path: "/" });
  }
});

function getUser() {
  return Vue.prototype.$Amplify.Auth.currentAuthenticatedUser()
    .then(data => {
      if (data && data.signInUserSession) {
        AmplifyStore.commit("setUser", data);
        return data;
      }
    })
    .catch(e => {
      console.info(e);
      AmplifyStore.commit("setUser", null);
      return null;
    });
}

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
    meta: { requiresAuth: false }
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
    meta: { requiresAuth: true }
  },
  {
    path: "/profile",
    name: "Profile",
    component: Profile,
    meta: { requiresAuth: true }
  },
  {
    path: "/login",
    name: "Authenticator",
    component: AmplifyAuth,
    meta: { requiresAuth: false }
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});

router.beforeResolve(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    user = await getUser();
    if (!user) {
      return next({
        path: "/login",
        query: {
          redirect: to.fullPath
        }
      });
    }
    return next();
  }
  return next();
});

export default router;

/loginページで呼び出すコンポーネント、

<template>
  <div>
    <amplify-authenticator :authConfig="authConfig"></amplify-authenticator>
    <amplify-sign-out v-bind:signOutConfig="signOutConfig"></amplify-sign-out>
  </div>
</template>

<script>
export default {
  data() {
    return {
      authConfig: {
        usernameAttributes: "Email",
        signUpConfig: {
          header: "Sign Up",
          hideAllDefaults: true,
          defaultCountryCode: "1",
          signUpFields: [
            {
              label: "Email",
              key: "username",
              required: true,
              displayOrder: 1,
              type: "string"
            },
            {
              label: "Password",
              key: "password",
              required: true,
              displayOrder: 2,
              type: "password"
            }
          ]
        }
      },
      signOutConfig: {
        msg: "Sign Out",
        signOutButton: "Sign Out"
      }
    };
  }
};
</script>

サインインにemail、サインアップ時にemail、パスワードを要求するには上記のような設定になる。

次にプロフィール画面、Profile.vue 、 ユーザー名を表示する

<template>
  <div class="container">
    <h1 v-if="user">{{ user.username }}'s profile</h1>
    <div></div>
  </div>
</template>

<script>
import AmplifyStore from "@/store/index";
export default {
  name: "Profile",
  methods: {},
  computed: {
    user: function() {
      return AmplifyStore.state.user;
    }
  }
};
</script>

store/index.js : Vuexの設定、ユーザー名を全体で持つ。

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    setUser(state, user) {
      state.user = user;
    }
  },
  actions: {},
  modules: {}
});

App.vue : router-link設定

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link>|
      <router-link to="/about">About</router-link>|
      <router-link to="/profile">Profile</router-link>|
      <router-link to="/login">Login</router-link>
    </div>
    <router-view />
  </div>
</template>

<style lang="scss"></style>

これで一応ログイン画面が出てくる。

実際にサインアップとサインインしてみる。

「サインイン」のときは、メールアドレスと、パスワード。

サインアップを入力してボタンを押すと、コード認証画面になる。メールでコードが送られている。コードを画面に入力する。

コードを入力するとサインイン画面になるので、メールアドレスと、パスワードでサインインする。

これでサインイン完了。プロフィール画面とかにアクセスできるようになる。

とりあえずここまで。

authのログイン設定の変更。

add auth作る時に エンドユーザーをどのようにサインインさせるか、と必須の属性(メアドや住所)の設定をした。

ユーザープールでのログイン設定は、一度作ると変更できないようだ。一旦消して、作り直す必要がある。

amplify auth remove
amplify push

amplify add auth

 Do you want to use the default authentication and security configuration? Default configuration
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? Yes, I want to make some additional changes.
 What attributes are required for signing up? Email
 Do you want to enable any of the following capabilities?

amplify push

今の所自分がやりたいのが、ログイン時は(メールアドレスとパスワード)。サインアップ時に(ユーザー名、メールアドレス、パスワード登録)、なんだけど、これやり方がわからない。

まあいいか、そんな細かいところは。また後で。