Auth0 サンプルの SPA を微修正して、 Auth0 サンプルの API に CORS していく

# はじめに

前回までにこんなことをやってみました。

- [今更ながら Auth0 およびサンプルアプリケーションを触ってみる](https://qiita.com/keiichi-hikita/items/f66c9bd23bdaae526723)
- [Auth0 で認証される API を作って使ってみる](https://qiita.com/keiichi-hikita/items/c997492f750d483f7817)
- [Auth0 で認証する SPA を作ってみる](https://qiita.com/keiichi-hikita/items/970af57fd78c421776c7)


扱いたいと思っていた Auth0 のサンプルアプリケーションは一通り触れたので、今回はサンプル SPA から、同じくサンプル API に対して CORS してみようと思います。

# 今回の構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/f93149b3-2d15-e844-7c24-9978a8e34003.png)

このように、サンプル SPA を `localhost:3000` で動作させ、そこから `localhost:8080` のサンプル API にCORS接続するというものです。


# API 側の改修

API 側はデフォルトだと SAME ORIGIN での受付しかできないので、CORS 対応にしていきます。

`cors` をインストールします。

```bash
npm install cors --save
```

次に [Auth0 で認証される API を作って使ってみる](https://qiita.com/keiichi-hikita/items/c997492f750d483f7817) で作成した `api.js` を以下のように編集します。

```javascript:api.js
const express = require('express');
const app = express();
const { auth } = require('express-oauth2-jwt-bearer');

const port = process.env.PORT || 8080;

const cors = require('cors'); // <-- 追加
app.use(cors());              // <-- 追加

const jwtCheck = auth({
  audience: 'https://auth0.com/[Auth0 Project ID]/api/auth0-practice-api',
  issuerBaseURL: 'https://[Auth0 Project ID].us.auth0.com/',
  tokenSigningAlg: 'RS256'
});

// enforce on all endpoints
app.use(jwtCheck);

app.get('/authorized', function (req, res) {
    res.json({message: 'Secured Resource'}); // <-- ちょっと修正
});

app.listen(port);

console.log('Running on port ', port);
```

API 側の修正はこれだけです。
jwtChesk の前に `cors` を `app.use` で埋め込みます。

あとは以下の通り起動しておきます。

```bash
% node api.js                                                                          
Running on port  8080
```

# SPA 側の改修

`axios` だけインストールしました。

```bash
npm intall axios --save
```

[Auth0 で認証する SPA を作ってみる](https://qiita.com/keiichi-hikita/items/970af57fd78c421776c7) で作成したサンプルに以下を追加します。


## Practice.vue の追加

ページとしては 1 ページ追加をしました。

1 年半触ってなかったので、 Composition API の使い方自体忘れてました。
というか、今も現在進行系で忘れてます。
...というより Vue3 自体を (略)

```vue:views/Practice.vue
<template>
  <div>
    {{ response }}
  </div>  
</template>

<script setup lang="ts">
import { onMounted } from 'vue';
import { useAuth0 } from '@auth0/auth0-vue'
import { ref } from 'vue'
import axios from 'axios'

const { getAccessTokenSilently } = useAuth0()
const token = ref('')
const response = ref({})
const options = ref({})

onMounted(async () => {
  token.value = await getAccessTokenSilently()
  options.value = {
    headers: {
      Authorization: `Bearer ${token.value}`
    }
  }

  const temp = await axios.get('http://localhost:8080/authorized', options.value)
  console.log(temp)
  response.value = temp.data.message
})
</script>
```

ルーティングの追加。

```javascript:src/router/index.ts
import { createRouter as createVueRouter, createWebHashHistory, Router } from "vue-router";
import Home from "../views/Home.vue";
import Profile from "../views/Profile.vue";
import Practice from "../views/Practice.vue";
import { createAuthGuard } from "@auth0/auth0-vue";
import { App } from 'vue';

export function createRouter(app: App): Router {
  return createVueRouter({
    routes: [
      {
        path: "/",
        name: "home",
        component: Home
      },
      // 追加 ここから
      {
        path: "/profile",
        name: "profile",
        component: Profile,
        beforeEnter: createAuthGuard(app)
      },
      // 追加 ここまで
      {
        path: "/practice",
        name: "practice",
        component: Practice,
        beforeEnter: createAuthGuard(app)
      }
    ],
    history: createWebHashHistory()
  })
}
```

実は Nuxt 経由でしか使ったこと無いので、初めてまともにルーティング書きました。

`main.ts` の Auth0 設定に `audience` を追加します。

```javascript:src/main.ts
import App from "./App.vue";
import { createApp } from "vue";
import { createRouter } from "./router";
import { createAuth0 } from "@auth0/auth0-vue";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faLink, faUser, faPowerOff } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import authConfig from "../auth_config.json";
import hljs from 'highlight.js/lib/core';
import json from 'highlight.js/lib/languages/json';
import hljsVuePlugin from "@highlightjs/vue-plugin";
import "highlight.js/styles/github.css";

hljs.registerLanguage('json', json);

const app = createApp(App);

library.add(faLink, faUser, faPowerOff);

app
  .use(hljsVuePlugin)
  .use(createRouter(app))
  .use(
    createAuth0({
      domain: authConfig.domain,
      clientId: authConfig.clientId,
      authorizationParams: {
        redirect_uri: window.location.origin,
        audience: authConfig.audience // <-- 追加
      }
    })
  )
  .component("font-awesome-icon", FontAwesomeIcon)
  .mount("#app");
```

最後に `auth_config.json` に対して Audience を追加します。

```json:auth_config.json
{
  "domain": "[Auth0 Project ID].us.auth0.com",
  "clientId": "[SPA CLIENT ID]", 
  "audience": "https://auth0.com/[Auth0 Project ID]/api/auth0-practice-api"
}
```


あとは以下の通り起動しておきます。

```bash
% npm run serve
```

# 動作確認

ブラウザから `http://localhost:3000/#/practice` に接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/d3c8529c-7b39-2cb3-aaaf-bf2970fcc8d3.png)


こんな感じで、レスポンスに含まれる `message` 部分が表示されたので OK かと思います。
とにかく認証周りのコードをスッキリ書けるのが良いですね。

次は認可周り (Permission とか Role とか) をいじってみたいと思ってます。
が、その前にちょっと Auth0 CLI 周りも気になる。