【NuxtJS】keycloak-js導入手順

### 前提
・Nuxtプロジェクト作成済

https://qiita.com/crml1206/items/9721e842727834707825

・Keycloakコンテナ(jboss/keycloak)構築済

https://qiita.com/crml1206/items/07d029e9fae488af08e2

---
### 依存追加

```
yarn add nuxt-property-decorator
yarn add keycloak-js
```

---
### ソースコード
```typescript
<template>
  <v-container>
    <v-row>
      <h1>管理画面</h1>
    </v-row>
  </v-container>
</template>

<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator";
import { KeycloakInstance, KeycloakConfig } from "keycloak-js";
import Axios from "../infras/AxiosFactory";

@Component
export default class AdminTop extends Vue {
  async mounted() {
    // Keycloakインスタンスを作成するための情報（※環境ごとに切り替えられるようjsonファイルに外出し推奨）
    let initOptions = {
      url: "http://localhost:8180/auth",
      realm: "user",
      clientId: "nuxt-app",
    };
    // Keycloakインスタンス作成
    let keycloakClient = await this.createKeycloakClient(initOptions);
    // 認証処理
    await keycloakClient.init({
      onLoad: "login-required",
    });
    // 取得したIDトークンをAuthorizationヘッダに格納
    Axios.defaults.headers.common.Authorization = `Bearer ${keycloakClient.idToken}`;
  }

  async createKeycloakClient(_config: KeycloakConfig): Promise<KeycloakInstance> {
    const { default: Keycloak } = await import("keycloak-js");
    return Keycloak(_config);
  }

}
```
---
### 解説
画面を表示する（NuxtJSのライフサイクルのfetchが実行される）タイミングで、Keycloak認証を行う。

画面を表示するタイミングでKeycloak認証を実施する理由は、後述する認可コードフローではIDトークンを取得するために２回リクエストを送る必要があり、例えばボタン押下式にすると「1回目ボタンクリック→認証OK」「2回目ボタンクリック→IDトークン取得」のような手間をとることになってしまうため。

Keycloak認証タイミングは他にも色々あると思われるが、本記事では「画面表示されるタイミング」で行っている。

#### IDトークンを取得するまでの処理フロー
　o1. アプリ画面を表示する際にfetch()が実行される
　o2. keycloakClient.init() ※**認証認可リクエスト**
　　↓ アプリ画面 から Keycloakログイン画面 に リダイレクト
　o3. ログイン認証実施
　　↓ Keycloakログイン画面 から アプリ画面 にリダイレクト（再びfetch()の処理が走る）
　o4. keycloakClient.init() ※**トークンリクエスト**
　o5. o4のトークンリクエストで取得したIDトークンをAuthorizationヘッダに格納
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/2f56562d-ab6d-1c67-4fa3-edd86f4631dc.png)

上記画像は下記サイトより拝借させていただいた。分かりやすくまとまっていて参考になった。

https://qiita.com/TakahikoKawasaki/items/4ee9b55db9f7ef352b47

↑の記事でまとめられている通り、Open ID Connectのフローは色んなパターンがあるが今回は「**response_type = code(認可コード) ＆ scope = openid**」のパターンであるため、IDトークンを取得するために「認証認可リクエスト→トークンリクエスト」の順で処理が行われる。

##### 認証認可リクエスト時のURL
response_type=code　と scope=openid　がパラメータとして渡されている
```
http://localhost:8180/auth/realms/user/protocol/openid-connect/auth?client_id=nuxt-app&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Flogin&state=4ed3124b-fc45-4feb-a04f-f1428077bb5f&response_mode=fragment&response_type=code&scope=openid&nonce=ed753981-9909-4fd4-8611-5587066a9fb4
```

##### トークンリクエスト時のURL

```
http://localhost:8180/auth/realms/user/protocol/openid-connect/token
```


### 理解するのに時間がかかってしまったところ
・keycloakClient.init()は「認証認可リクエスト」をする時と「トークンリクエスト」をする時の両方で使われる。
・「認証認可リクエスト」を実行するときは、Keycloakのログイン画面にリダイレクトするのでinit()以降の処理は実行されないこと。

### Keycloakでどのフローを利用するかの設定方法
・Open ID Connectのフローとして、今回取り上げた「認可コード」に加えて「インプリシット」が存在しており、
　この２つは下記画面の赤丸部分「Standard Flow Enabled(認可コード)」「Implicit Flow Enabled(インプリシット)」で設定可能。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/efd45739-e8ce-49fb-481d-2ce96f44b7fe.png)