INFRA

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

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

アカウント乗っ取りを防ぐためには二段階認証は有効です。Auth0を利用すると、多要素認証の機能を簡単に導入することができます。https://t.co/uUh9ixHzga

ぜひ、ウェブサイトよりお気軽にご相談ください。#二段階認証 #多要素認証

— Auth0 Japan (@auth0_jp) July 8, 2019

auth0-spa-js

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

週間DL数は10万以上、Auth0を選んだほぼすべてのお客様に様々な用途でお使いいだだいており、デフォルトSDKとして提供しているブラウザベースSDK auth0.jsに加え、よりシングルページアプリケーション(SPA)に最適化された新しいJavaScript SDK、auth0-spa-jsがリリース!https://t.co/pAG0hepz7a

— Auth0 Japan (@auth0_jp) August 6, 2019

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

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

つくったもの

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

https://github.com/edo1z/auth0-studygithub.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にリダイレクトしてます。それ以外はホームにリダイレクトします。