Azure App Service の Easy Auth で Entra ID 認証する設定のまとめ

Microsoft Entra ID (旧 Azure AD) アカウントでユーザ認証する Web アプリケーションを Azure 上で構築したい場合、Azure App Service の組み込み認証 (Easy Auth) を使うと簡単に実現できます。

しかし簡単と言いつつも Entra ID 連携の設定には罠がいくつかあって、ハマるとえらい面倒なことになります。

この記事では、一連の基本的な設定手順とカスタマイズ方法について自分が知っていることをまとめます。

:::note warn
試してみた経験に基づいてこの記事を書いているので **「少なくとも2023年12月時点ではこうやったらうまくいった」** 程度の情報として捉えていただきたいです。誤りの指摘や補足情報などのコメントをいただけるとありがたいです。切実に。
:::

:::note warn
- この記事ではシングルテナントの Entra ID と連携する手順を扱います。マルチテナントのことは知らないです。
- この記事では主に Azure Portal から操作する手順を扱います。Terraform でやる手順は知らないです。
:::

## 基本設定
最初に App Service の Easy Auth を使って Entra ID 連携を設定する方法を書いておきます。

### 1. App Service を作成する
調べたらたくさん情報が出てくるので割愛します。

### 2. Easy Auth の設定
他の ID プロバイダはどうか知らないですが、少なくとも Microsoft アカウントで認証させる設定の場合、その実態は **「App Service に "Entra ID アプリケーション" を紐付ける」** という操作になります。

つまり App Service とは別に「Entra ID アプリケーション」というオブジェクトを作成し、それと App Service を紐付ける、ということなのですが、これには2パターンのやり方があります。

- 【パターン1】 あらかじめ Entra ID アプリケーションを作成しておき、それを App Service に紐付ける
    - → 設定画面の「既存のアプリの登録を選択する」
- 【パターン2】 Azure Portal の App Service の認証設定の画面から Entra ID アプリケーションもまとめて作成してしまう
    - → 設定画面の「アプリの登録を新規作成する」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/53649e71-9c53-40e3-bae0-478e1ee85b3c.png)

重要なことですが、 **圧倒的に 【パターン2】 がオススメ** です。

【パターン2】 では App Service 側で Entra ID アプリケーションの細かい設定をすべてやってくれるので、「とりあえず動く」までは簡単にたどり着けます。【パターン1】 では Entra ID アプリケーションの細かい設定をすべて手作業でやる必要があり、調べながらひとつひとつやっているとかなり時間がかかります。 (かかりました...)

:::note info
**【パターン2】 のときに自動で設定してくれる内容の例**

- プラットフォームの追加とリダイレクト URI の設定
- ID トークン送信の許可
- クライアントシークレットの作成と App Service 側への設定
- 最低限必要な API アクセス許可の追加
- API の公開スコープの追加と App Service 側への設定
- App Service 側の「発行者の URL」の設定
:::

【パターン2】 の懸念点は「Entra ID の管理権限が必要 (サブスクリプションの管理権限を持っているだけでは不可)」という点ですが、どのみち次のステップで Entra ID 管理者の操作が必要となるため、最初から Entra ID 管理者を引っ張り出してきて全部一緒に作業をしたほうがいいです。

### 3. アプリ側で必要な「アクセス許可」を確定する
このステップは次の `4. 「管理者の同意」を得る` の前に必ずやりましょう。あとから変更する場合は再び「管理者の同意」を得る必要が生じます。(あなたが Entra ID 管理者であるなら特に問題はありません)

App Service から ID プロバイダを追加するとき、もしくは Entra ID アプリケーションの設定画面から、下記画像のような「API のアクセス許可」設定ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/6d8df7e8-ef5d-c7dc-6223-fd87b39455f6.png)

この設定は初期状態だと `User.Read` しかついていないのですが、 まず最初に Microsoft Graph の `email` `offline_access` `openid` `profile` の4種類は追加しておいたほうがよいです。**どうせ大抵の場合はこれらのアクセス許可が必要になります。**

この他にも必要なアクセス許可が判明している場合は、ここで追加します。

### 4. 「管理者の同意」を得る
Entra ID 管理者の出番です。

この手順を実施するには Entra ID の「グローバル管理者」または「特権ロール管理者」、最低でも「クラウドアプリケーション管理者」の権限が必要になります。[^grant-admin]

[^grant-admin]: [アプリケーションに対してテナント全体の管理者の同意を付与する - Microsoft Entra ID | Microsoft Learn](https://learn.microsoft.com/ja-jp/entra/identity/enterprise-apps/grant-admin-consent)

手順そのものは簡単で、Entra ID アプリケーションの「API のアクセス許可」画面を開いて「{アプリ名} に管理者の同意を与えます」を押すだけです。

:::note info
実はこの設定はやらなくても「ユーザが都度、承認リクエストを管理者に送信する」という方式もとれるらしいのですが、管理者の手間が増えるだけなので大抵の場合は選択せず、アプリケーション全体に「管理者の同意を与える」のが一般的かと思います。アプリの利用ユーザを制限したい場合は、管理者の承認ではなく、後述する別の方法で実現するのがよいです。
:::

### 5. EasyAuth の loginParameters の設定
ここまでの設定で、テナントのユーザが Web アプリにアクセスして Microsoft アカウントでログインすることが出来るようになって「できたー！」と思うかもしれませんが、 **罠です。**

Easy Auth からアプリケーションへのリクエストには　`x-ms-token-aad-*` ヘッダが付与されており、ここにログインユーザに関する様々な情報が付与されている [^aad-token] のですが、これをよく見ると例えば以下のような問題があることがわかります。

[^aad-token]: [Azure App Service 認証で OAuth トークンを操作する - Azure App Service | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/app-service/configure-authentication-oauth-tokens)

- API のアクセス許可に `email` を入れたにも関わらず ID トークン内に `email` が含まれていない
    - ユーザのメールアドレスをアプリケーション側で扱いたい場合に困る (※ ただし `email` をユーザ ID として使用することを検討しているならやめたほうがよく、その場合は `oid` を使用するのがよい)
- API のアクセス許可に `offline_access` を入れたにも関わらずリフレッシュトークンが送られてこない
    - EasyAuth セッションは1時間で切れてユーザ情報が送られてこなくなるので、アプリケーション側のセッションでユーザ情報を保持していない場合などにトークン更新ができないと、ユーザに再認証を求る必要が生じる

これについては正確なことはわからないですが、推測するに **「Entra ID 側の "この権限をアプリに与えていいぞ" 設定」** と **「Easy Auth 側の "この情報をアプリに送るぞ" 設定」** は異なるのではないか？ と思っています。

前者の設定は前述の「3. アプリ側で必要な「アクセス許可」を確定する」でやりました。ややこしいのは、 **後者の設定は Azure Portal からはできないっぽい** ということです。

これには Azure CLI を使う方法と Azure Resource Explorer を使う方法があるらしいですが、ここでは Azure Resource Explorer を使う方法を書きます。

**→ [Azure Resource Explorer](https://resources.azure.com/)**

Azure Resource Explorer では、Azure のリソース構造を直接参照したり編集したりすることが出来ます。

まず、 Azure Resource Explorer で `subscriptions/{SubscriptionID}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Web/sites/{AppName}/config/authsettingsV2` というリソースファイルを開きます。これはどうやら Easy Auth のバージョン2に関する設定ファイル (？) のようです。

※ サイドバーを何回もポチポチするのが面倒な場合は、以下のような URL を組み立てて Web ブラウザからアクセスする、という手順でも開けます。
`https://resources.azure.com/subscriptions/7{SubscriptionID}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Web/sites/{AppName}/config/authsettingsV2/list`

このファイルを編集して、 `properties` > `identityProviders` > `azureActiveDirectory` > `login` というブロックの中に下記内容を追記します。

``` json:authsettingsV2
          "loginParameters": [
            "response_type=code id_token",
            "scope=email openid offline_access profile https://graph.microsoft.com/User.Read"
          ]
```

これを保存すれば、Easy Auth からアプリケーションに情報が送られるようになるはずです。

## カスタマイズ設定
「こういうことをやりたいときはこうする」を書いていきます。

### アクセスできる ユーザ/グループ を制限したい
初期設定ではテナントのユーザは全員アプリケーションにログインすることが出来ますが、これを一部のメンバーやグループのみに制限したい場合は、Entra ID の「エンタープライズアプリケーション」画面から下記2点の設定をします。

- `プロパティ` > `割り当てが必要ですか?` を `はい` に設定
- `ユーザとグループ` でアクセスを許可したいユーザまたはグループを指定

### 「部署名」などのユーザ属性情報をアプリケーション側で扱いたい
Entra ID に登録されている「部署名」などのユーザ属性情報をアプリケーションに渡したい場合、下記2点の設定をします。

- `Entra ID` > `アプリの登録` > `マニフェスト` で `acceptMappedClaims` を `true` に変更
- `Entra ID` > `エンタープライズアプリケーション` > `シングルサインオン` > `属性とクレーム` で必要な属性を指定してクレームを追加

クレームを追加するとき、要求の「名前」がそのままアプリケーションに渡されるときの key になるので注意しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/13458c1e-9072-ecc2-4c6c-6625f6fb01dc.png)

設定が完了すると、アプリケーションに渡される ID トークンの中に要求したクレームの情報が追加されるようになります。

### 「認可」を実現したい
例えば「このアプリケーションの管理者と指定したユーザは管理画面を利用できる」といった、ユーザごとに異なる機能を提供したいといったケースはよくあると思います。

今回の Easy Auth + Entra ID 連携ではテナントユーザの「認証」は行いますが、「認可」の仕組みは提供されません。しかし「グループクレーム」を使用することで、権限管理の機能に関しては Entra ID 側に任せることが出来るようになります。

:::note warn
詳しくはわかっていないですが、このへんの操作も Entra ID 管理者権限が必要になる可能性があります。
:::

1. `mywebapp-administrator` のような名前の Entra ID セキュリティグループを作成し、アプリケーションの管理者として指定したいユーザをこのグループに追加する
2. `Entra ID` > `アプリの登録` > `トークン構成` > `グループ要求の追加` で、セキュリティグループの要求を追加
3. `Entra ID` > `アプリの登録` > `マニフェスト` で `acceptMappedClaims` を `true` に変更
4. `Entra ID` > `エンタープライズアプリケーション` > `シングルサインオン` > `属性とクレーム` でグループクレームのフィルタリング設定を追加 (下記画像を参考)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/e5b039f5-6a7d-ed56-0719-4cc3a4cee469.png)

こうすることで、アプリケーションにログインしたユーザが `mywebapp-` から始まる Entra ID グループに所属していた場合に、その「グループに所属している」という情報をアプリケーション側で受け取ることが出来ます。(ID トークン内の `groups` にセキュリティグループのオブジェクト ID 一覧が含まれるようになります。)

Entra ID グループによって権限を表現することで、アプリケーション側ではユーザの権限に応じた機能を提供することができるというわけです。