Azure ADの認証をReactアプリケーションに追加してみた。

# はじめに

今回は、ReactアプリケーションにAzure ADを活用した認証を追加して見たいと思います。

# Azure ADの設定

## アプリケーションの作成

Azure AD に今回のアプリケーションを追加し、設定していきます。

![Screen Shot 2022-11-08 at 10.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/c634ed84-76d8-6055-0609-84e517634285.png)

![Screen Shot 2022-11-08 at 10.23.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/a055d151-0daf-fed7-a027-aa88a2da422d.png)
![Screen Shot 2022-11-08 at 10.37.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/4c37961d-e97c-f826-99cf-a64eaa0bd275.png)

Redirect URL は React のアプリケーションが動いているホストを指定する。今回はローカル開発環境下で動いているので、localhostを指定する。React にRedirect専用のページを用意するならそのpath 含めて指定する。

![Screen Shot 2022-11-09 at 11.12.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/a9e82089-f30e-4b65-3e13-860cfb60f338.png)

Azure AD にアプリケーションの追加ができたら、`Application (client) ID`が発行される。このIDはReactからAzure ADに通信をするために必要なので、メモしておこう。　
![Screen Shot 2022-11-08 at 10.49.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/16f396b8-c453-2193-75ef-1afccd0c9945.png)

## アプリケーションの設定

![Screen Shot 2022-11-08 at 10.52.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/77a64667-848c-ca97-740a-9884a3873447.png)
認証が成功した際の発行するトークンの種類(アクセストークンとIDトークン)の設定をします。React(SPA) アプリケーション場合は、両方有効化する必要があります。（と書いています）僕はこれに気づかず、かなりつまりました。

![Screen Shot 2022-11-08 at 10.54.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/ef946cdc-7b74-e4ec-9d89-247522e515bc.png)

## ユーザの作成
テスト用の新しいユーザをAzure ADに追加します。

![Screen Shot 2022-11-08 at 13.53.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/d5c2bdb5-e2aa-75e7-a1c1-b465486c48a4.png)
![Screen Shot 2022-11-08 at 13.47.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/27076723-4237-1f80-78c1-ba960932a96a.png)
![Screen Shot 2022-11-08 at 13.59.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/f84fb485-fb84-1ce9-14b4-df1346130e00.png)

ログインするときに必要なユーザーネームは、ここで設定した任意の名前と`Primary domain`の組み合わせです。この情報も必要なのでメモしておこう。
パスワードは、一時的なパスワードなので、初回ログイン時にまた新たにパスワードの設定を求められます。だからこの時点では適当なパスワードか`Auto-generate password`を選択して自動生成しよう。

![Screen Shot 2022-11-08 at 14.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/988c0cd7-7934-61c6-fa87-69365a651011.png)

ユーザの作成ができた瞬間は画面に反映されないので、`Refresh`ボタンをクリックしてみてください。おそらく先程作ったユーザが出てくるので。

![Screen Shot 2022-11-08 at 14.08.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/fd3a3432-ea78-38d8-11b2-143155bc450e.png)

こんな感じに。

![Screen Shot 2022-11-08 at 14.10.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/2bf9f883-0597-d54b-0691-52e8d002f9f6.png)


# React プロジェクトの作成

## viteを使ってReactプロジェクトを作る

開発ツールとして、Vite を使います。Webpack と比べて、かなり軽くて高速です。

https://vitejs.dev/

typescript のテンプレートが用意されているので、以下のコマンドでテンプレートから React プロジェクトを生成します。

```bash
npm create vite@latest azure-ad-auth-react -- --template react-ts
```

Create React App と違ってライブラリーのインストールまではしてくれないので、`npm install`をします。

```bash
cd azure-ad-auth-react && npm install
```

`package.json`に開発用の実行`dev`スクリプトが定義されているので、実行します。もちろんホットリロードが効きます。

```bash
npm run dev
```

```bash
❯ npm run dev

> azure-ad-auth-react@0.0.0 dev
> vite


  VITE v3.2.3  ready in 337 ms

  ➜  Local:   http://127.0.0.1:5173/
  ➜  Network: use --host to expose
```

無事Reactが動きました。ものすごく簡単、そして高速です。

![Screen Shot 2022-11-08 at 10.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/05ba4b2c-cf32-68fb-69c2-ed4f55584888.png)

開発サーバのポート番号を変更します。Azure AD で登録したRedirect URLにあわせます。今回は3333。

```ts:vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3333
  }
})
```

無事変更されました。 
![Screen Shot 2022-11-09 at 15.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/9777b59f-48b9-a7a4-f60c-0410edc8855f.png)


## Microsoft Authentication Libraries (MSAL)を使ってAzure AD に認証する

Azure ADとの認証を円滑にするために、オープンソースプロジェクトとして、Microsoft Authentication Libraries (MSAL)というものがあります。遠慮なく使わさせてもらいます。今回はReactを使ってブラウザーから直接認証を行うので、MSALの一部である`@azure/msal-browser`というものをインストールして、使っていきます。

https://www.npmjs.com/package/@azure/msal-browser

```bash
npm install -E @azure/msal-browser
```

viteのテンプレートとして生成された`App.tsx`をそのまま編集していきます。

```diff_tsx:src/App.tsx
import { useState } from "react";
+ import { AccountInfo, PublicClientApplication } from "@azure/msal-browser";
import reactLogo from "./assets/react.svg";
import "./App.css";

+ type Account = Partial<AccountInfo>;

function App() {
  const [count, setCount] = useState(0);
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const [error, setError] = useState<any>(null);
+ const [user, setUser] = useState<Account | null>(null);

+ const publicClientApp = new PublicClientApplication({
+    auth: {
+     clientId: "ac40c95c-6e6c-4188-bee7-ac02b425fbc1",
+      redirectUri: "http://localhost:3333",
+      authority:
+        "https://login.microsoftonline.com/ryuichinishidevgmail.onmicrosoft.com/",
+    },
+    cache: {
+      cacheLocation: "sessionStorage",
+      storeAuthStateInCookie: true,
+    },
+  });

+  const login = async () => {
+    try {
+      const res = await publicClientApp.loginPopup({
+        scopes: ["user.read"],
+        prompt: "select_account",
+      });
+      setIsAuthenticated(true);
+      setUser({ ...res.account });
+    } catch (error) {
+      setIsAuthenticated(false);
+      setUser(null);
+      setError(error);
+    }
+  };

+  const logout = async () => {
+    await publicClientApp.logoutPopup();
+    setIsAuthenticated(false);
+    setUser(null);
+  };

  return (
    <div className="App">
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src="/vite.svg" className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.org" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
+      {isAuthenticated ? (
+        <div>
+          <button onClick={logout}>logout</button>
+        </div>
+      ) : (
+        <div>
+          <button onClick={login}>login</button>
+        </div>
+      )}
+      {error ? <div>{error.message}</div> : null}
+      <br/>
+      {user ? <div>Hello, {user.username}</div> : null}
    </div>
  );
}

export default App;
```

基本的に`@azure/msal-browser`の`PublicClientApplication`のインスタンスを通して、Azure AD とやり取りします。このインスタンスさえ作成してしまえば簡単に認証処理ができます。クラスのコンストラクタに指定する値は:

- auth.clientId => 先程メモした`Application (client) ID`
- auth.redirectUri => 先程自ら設定したRedirect URL
- auth.authority => 'https://login.microsoftonline.com/{Primary Domain}' `Primary Domain` は Azure AD のoverviewに記載されています。

キャッシュの設定は、sessionStorageに保存して、authのstateはCookieとして保存する。

```tsx
const publicClientApp = new PublicClientApplication({
    auth: {
     clientId: "ac40c95c-6e6c-4188-bee7-ac02b425fbc1",
      redirectUri: "http://localhost:3333",
      authority:
        "https://login.microsoftonline.com/ryuichinishidevgmail.onmicrosoft.com/",
    },
    cache: {
      cacheLocation: "sessionStorage",
      storeAuthStateInCookie: true,
    },
  });
```

`public.loginPopup` functionを実行するとlogin ページのポップアップがでてきて、そのまま簡単に認証ができます。`scopes`に権限を指定できます。今回はとりあえず read アクセスだけ。認証が成功すると、発行されたトークンを含め、デコードされたアカウントのオブジェクトが返ってくるので、その値をステートに登録しています。

```tsx
 const login = async () => {
    try {
      const res = await publicClientApp.loginPopup({
        scopes: ["user.read"],
        prompt: "select_account",
      });
      setIsAuthenticated(true);
      setUser({ ...res.account });
    } catch (error) {
      setIsAuthenticated(false);
      setUser(null);
      setError(error);
    }
  };
```

ログアウト処理も、ものすごく簡単で、`publicClientApp.logoutPopup`を呼び出すだけで、ログアウトするためのポップアップページがでてきて、そこでログアウトができます。

```tsx
  const logout = async () => {
    await publicClientApp.logoutPopup();
    setIsAuthenticated(false);
    setUser(null);
  };
```

あとは、ステートの値に応じてUIの表示を変えています。ログインが成功すれば。logoutのボタンを表示させ、現在ログインしているアカウントの名前を表示、そうでなければloginボタンを表示。何かしらエラーが発生すれば、それを画面に表示する。

```tsx
      {isAuthenticated ? (
        <div>
          <button onClick={logout}>logout</button>
        </div>
      ) : (
        <div>
          <button onClick={login}>login</button>
        </div>
      )}
      {error ? <div>{error.message}</div> : null}
      <br/>
      {user ? <div>Hello, {user.username}</div> : null}
```

実際に実行してみます。

```bash
npm run dev
```

ログインボタンが表示されているので、クリックする。
![Screen Shot 2022-11-08 at 14.24.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/31e9c8d1-351e-4be5-1287-ed5a2f1b0c32.png)
ログインボタンをクリックすると、ログイン専用のポップアップページが出てくる。
![Screen Shot 2022-11-08 at 14.38.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/16b7f86e-e398-63c9-f935-f85296212bb0.png)
先程、新しく作成したユーザのユーザネームとパスワードを入力して、sign in ボタンをクリック。
![Screen Shot 2022-11-08 at 14.40.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/262ac2e4-fef5-8d2e-cfe0-a60e3ebb401c.png)
初回ログインなので、パスワードの変更を求められます。任意の新しいパスワードを入力する。
![Screen Shot 2022-11-08 at 14.43.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/6ae600f5-7c87-7c1b-3333-d1db684dcbb6.png)
ログインが成功すると、下にユーザネームが表示される。
![Screen Shot 2022-11-08 at 14.44.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/cdc3b4ed-66fb-b27c-fbc8-e2a122024a92.png)

# おわりに
Microsoft Authentication Librariesを利用することにより、思った以上に簡単にReactアプリケーションに認証機能を追加することができました。発行されたアクセストークンを使ってazureのリソースに簡単にアクセスすることができます。このようにAzureを活用すると安全なアプリケーションが爆速で開発できるようになります。