Azure App Service の Easy Auth で取得したアクセストークンで Microsoft Graph API を叩くまで

結論： ↓ このドキュメントを読めばできる

https://learn.microsoft.com/ja-jp/azure/app-service/tutorial-connect-app-access-microsoft-graph-as-user-javascript?tabs=azure-resource-explorer

この記事は上記ドキュメントのちょっとした補足をする程度のやつ。

## 背景
- Azure App Service で Web アプリを作ると、Easy Auth という機能で外部の IdP と連携した認証機能が簡単に作れる
- 認証すると Web アプリ側にアクセストークンが送られてくるので、適切にスコープなどを設定すればさまざまなデータが取れる
    - 例えば Azure AD 認証の場合だとリクエストヘッダで `x-ms-token-aad-access-token` が送られてくる

## 今回やりたいこと
- App Service で動いている Web アプリで Azure AD によるユーザ認証 (単一テナント) を行うようにしたい
- Web アプリ側からログイン中ユーザの名前とか所属組織とかが知りたい

## やりかた
### App Service に ID プロバイダの設定を追加する
まずは App Service に ID プロバイダの設定を追加する。
これについては何も説明することはなくて、Azure Portal からポチポチやれば終わる。何もハマるところはない。

ただし Azure AD のアプリケーション管理権限がない場合はアプリケーションIDやシークレットを入手して手動で入力しなければいけないので少し設定が難しい。この場合は下記の点に注意する。

- Azure AD アプリケーションのスコープに `Microsoft.Graph > User.Read` を追加する
    - 他にも Microsoft Graph API を使う際に必要なスコープがあるなら追加する
- ID プロバイダの設定で「発行者の URL」を記入する
    - しないと認証時にハマる
    - どんな値を設定したらいいかはググって欲しい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/0d3fae99-c283-17eb-8898-8a1659c56d03.png)

### 使用可能なアクセストークンを返すように App Service を構成する
正直まだこの手順がなぜ必要なのかわかっていない。誰か教えて欲しい。

この手順を踏まなくてもアプリケーションへのリクエストヘッダ `x-ms-token-aad-access-token` でランダム文字列っぽいアクセストークンは送られてくるが、このアクセストークンは何故か Graph API では使えない。(`CompactToken parsing failed with error code: 80049217` エラーが発生する)
この手順を踏むことで、送られてくるアクセストークンが JWT になって、Graph API が受け付けてくれるようになる。

以下、Azure CLI で作業する手順を書くが、Graph Explorer でも同じことはできる。

まず前提として `az` コマンドでサブスクリプションを設定しておく。
`az login` とか `az account set --subscription {SUBSCRIPTION_ID}` とかそのあたりで。

次に、対象 App Service の認証設定を JSON ファイルとして落としてくる。

``` console
$ az rest \
    --method GET \
    --url '/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEBAPP_NAME}/config/authsettingsv2/list?api-version=2020-06-01' \
    > authsettings.json
```

`SUBSCRIPTION_ID` とか `RESOURCE_GROUP` とかを埋めるのが面倒だが、Azure Portal の App Service の画面の URL が同じ構造になっているのでそこからコピペすればよい。

JSON ファイルを変更して、 `identityProviders` > `azureActiveDirectory` > `login` の箇所に下記の `loginParameters` を追記する。

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

ドキュメントの例には上記のようにスコープがいくつか記載されていて、これ全部必要なのだろうか？と思ったが、結果的には全部必要だった。

- `openid` SSO するために必要
- `offline_access` トークンを更新するために必要
- `profile` ユーザ情報を取得するために必要
    - これがないと `x-ms-client-principal-name` ヘッダなどが送られてこない
- `https://graph.microsoft.com/User.Read` Graph API を利用するときに必要なスコープ (？)

最後に、変更した JSON ファイルを使って App Service の認証設定を更新する。

``` console
$ az rest \
    --method PUT \
    --url '/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEBAPP_NAME}/config/authsettingsv2?api-version=2020-06-01' \
    --body @./authsettings.json
```

先日は `--headers "Content-Type=application/json"` をつけないと上記コマンドが通らなかった記憶があるが、いま試したらなくても通った。謎。

## 動作確認
リクエストヘッダについていたアクセストークンを利用して下記コマンドで Graph API にアクセスしたところ、所属している組織の一覧の情報を取得できた。 `User.Read` スコープだけでも所属組織の情報くらいなら取得できるらしい。

``` console
$ curl \
    -H 'Authorization: Bearer {ACCESS_TOKEN}' \
    https://graph.microsoft.com/v1.0/me/transitiveMemberOf
```