edo1z blog

プログラミングなどに関するブログです

Auth0のauth0-spa-jsをvueで使ってみた

Firebase Authには二段階認証がないようなので、Auth0を使おうと思います。Vue.jsで使います。

auth0-spa-js

spa用のよりシンプルにかけるSDK『auth0-spa-js』が最近公開されたとのことなので、使ってみました。

auth0-spa-jsのAPIリファレンスです。

@auth0/auth0-spa-js | @auth0/auth0-spa-js

つくったもの

つくったものは下記です。

github.com

createAuth0Clientが、Promiseを返すのでclient作る前に、色々なことが行われてしまうのを防ぐ必要がありますので、Vueインスタンス作成前に確実にclientが作られるようにしてみました。

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router/router'
import authService from './api/auth0'

(async function () {
  await authService.init();
  Vue.prototype.$auth = authService;
  Vue.config.productionTip = false;

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

上記のauthServiceは下記です。

src/api/auth0.js

import createAuth0Client from '@auth0/auth0-spa-js'

class AuthService {
  constructor() {
  }

  async init() {
    this.auth0 = await createAuth0Client({
      domain: process.env.VUE_APP_AUTH0_DOMAIN,
      client_id: process.env.VUE_APP_AUTH0_CLIENT_ID,
      redirect_uri: process.env.VUE_APP_AUTH0_REDIRECT_URI
    });
    await this.isLoggedIn();
  }

  async isLoggedIn() {
    this.loggedIn = await this.auth0.isAuthenticated();
    await this.getUser();
  }

  async getUser() {
    this.user = await this.auth0.getUser();
  }

  login(appState) {
    this.auth0.loginWithRedirect({appState: appState});
  }

  logout() {
    this.auth0.logout();
  }

  async callback() {
    const result = await this.auth0.handleRedirectCallback();
    await this.isLoggedIn();
    return result.appState;
  }
}

const service = new AuthService();
export default service;

loginは、リダイレクト型にしております。ログイン時にappStateを渡すことで、ログイン成功後にリダイレクトさせたいパスを入れておけます。handleRedirectCallbackを実行すると、appStateが返ってきます。また、このアプリは、vue-clieのcreateで作ってまして、createAuth0Clientに渡しているものは、.env.developmentの中身になります。

.env.development

VUE_APP_AUTH0_DOMAIN = "xxxxxxxxxx.auth0.com"
VUE_APP_AUTH0_CLIENT_ID = "xxxxxxxxxxxxxxx"
VUE_APP_AUTH0_REDIRECT_URI = "http://localhost:8080/#/callback"

routerでもAuthServiceを読み込んでログインチェックや、ログイン画面に飛ばしたりしてます。

src/router/router.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import AuthService from '@/api/auth0'

import Home from "../pages/Home"
import Profile from "../pages/Profile"
import Callback from "../components/Callback"
import NotFound from '../pages/NotFound'

Vue.use(VueRouter);

const routes = [
  {path: '/', component: Home, meta: {isPublic: true}},
  {path: '/callback', component: Callback, meta: {isPublic: true}},
  {path: '/profile', component: Profile},
  {path: '*', component: NotFound}
]

const router = new VueRouter({
  routes,
})

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.isPublic)) {
    next();
  } else if (AuthService.loggedIn) {
    next();
  } else {
    AuthService.login(to.fullPath);
  }
});

export default router

コールバック時のURLが、下記のようになっておりました。。

http://hoge.com?code=xxxxxxxxxxxxxxxxxxxxxxxxxx#/callback

これだと、別のページに飛ばしてもcodeとういクエリが残ったままになるので、仕方ないのでリダイレクトすることにしました。

src/components/Callback.vue

<template>
  <div>
    loading...
  </div>
</template>

<script>
  export default {
    async created() {
      try {
        const appState = await this.$auth.callback();
        const path = appState ? '/#' + appState : '/';
        location.href = path;
      } catch (e) {
        location.href = '/';
      }
    }
  };
</script>

appStateがある場合は、appStateにリダイレクトしてます。それ以外はホームにリダイレクトします。