しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

App Service Authentication と Entra ID で保護された Web API にアクセス可能な Access Token を取得する

App Service Authentication (Easy Auth) は非常に便利な機能なのですが、Web API をホストしている場合には他のアプリケーションから Service Principal を利用してアクセスしたいことがあります。

直近では自分が開発している Key Vault Acmebot というアプリで Web API を公開していますが、Easy Auth を有効化したまま Web API を呼び出したいという要望が多くて、とりあえずサンプル用意するかと思ったら地味にやり方を忘れていたのでブログに書いています。

ドキュメントに書かれていない気もしますが Easy Auth はリクエストに Bearer Token を付けて投げると、正しく検証してクレームをデコードしてくれるようになっています。これを使うと色々楽になります。

アプリケーション側の実装は Easy Auth を使っているので手を入れる必要がないですが、Entra ID 側のアプリケーション設定をほどほどに弄る必要があるので理解していないとはまります。

追記 : Entra ID に非常に詳しい @watahani さんからフィードバックを貰ったので、Client Credentials Flow で必要のない部分について修正を行っています。ブログも書いてくださったので是非読んでください。

ちなみに Key Vault Acmebot というアプリでは App Role を検証する機能が付いているので、有効化すると誰でもアクセス出来る状態ではなくなります。

App Service Authentication を有効化

まずは App Service Authentication を有効化して Microsoft Entra ID でのログインを行えるようにします。設定方法はドキュメントもありますし、Azure Portal からであれば簡単に行えるので特に深く説明はしません。

現在の Azure Portal を利用すると Entra ID の v2.0 エンドポイントが使われるので問題ありませんが、手動で設定する際には openIdIssuer が v2.0 エンドポイントになっているか確認しておきます。

自動生成された Application 設定を更新

Easy Auth の設定時に Entra ID 側に新しい Application が追加されますが、デフォルトの設定のままでは必要な Access Token が取得できないので、いくつかの設定を弄る必要があります。具体的には API スコープの公開と Access Token のバージョン変更が必要です。

API スコープの公開は Expose an API から Application ID URI などを設定するだけなので分かりやすいです。作成直後では空っぽですが Edit を選ぶと推奨される値が自動設定されるので保存して終わりです。

Application ID URI を設定するとスコープの値にも正しく反映されます。デフォルトでスコープは 1 つ設定されているので、後でこのスコープに対する許可を Service Principal に対して追加します。

追記 : Client Credentials Flow の場合はスコープの許可を追加する必要はありません。

最後に Application のマニフェストを編集して accessTokenAcceptedVersion の値を 2 に設定します。この設定で発行される Access Token のバージョンが 2 に更新されてフォーマットが大きく変わるため、Easy Auth でも正しく検証できるようになります。

バージョンを未設定の場合には Easy Auth では検証できないフォーマットとなるので認証が通りません。地味にはまりやすいポイントなのでバージョンについては気を付けたいところです。

Service Principal を作成し権限を追加(追記あり)

これで Web API 側の Application 設定は全て完了したので、ここからは Service Principal 周りの設定を行って必要な権限を持った Access Token を取得出来るようにします。

追記 : Client Credentials Flow を利用する場合には Service Principal に対してスコープを追加する必要はなく、同一テナントに存在するアプリケーションであれば全てにアクセス可能になります。

Service Principal の作成方法は書かないので、以下のドキュメントを参照して作成しておいてください。

作成した Service Principal で必要な作業は API Permissions で Web API 側の Application のスコープを追加することです。Add a permission から Web API の該当スコープを探して追加すれば完了です。

後は Client Credentials Flow で利用するための Client Secret を生成しておくぐらいで完了です。

MSAL を利用して Access Token を取得

最後は作成した Service Principal と MSAL を利用して Web API にアクセス可能な Access Token を取得します。今回は Client Secret を利用して Access Token を取得するので Client Credentials Flow となります。

例によって MSAL の使い方は公式ドキュメントに任せるので、経験が無い方はこちらを参照してください。

コードとしては ConfidentialClientApplicationBuilder を使う方法なので比較的簡単ですが、スコープの扱いだけ注意が必要です。Access Token を取得して Web API を呼び出すサンプルは以下のようになります。

using System.Net.Http.Headers;

using Microsoft.Identity.Client;

var app = ConfidentialClientApplicationBuilder.Create("<client id>")
    .WithClientSecret("<client secret>")
    .WithTenantId("<tenant id>")
    .Build();

var token = await app.AcquireTokenForClient(new[] { "<application uri>/.default" }).ExecuteAsync();

Console.WriteLine(token.AccessToken);

var httpClient = new HttpClient();

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);

var response = await httpClient.GetStringAsync("https://***.azurewebsites.net/api/certificates");

Console.WriteLine(response);

AcquireTokenForClient に Web API のスコープを渡していますが、今回のようなアプリケーション向けにトークンを取得する場合には .default で終わるスコープを指定する必要があります。

詳細は以下のドキュメントに記載されていますので、こちらを参照してください。

このコードを実行すると Access Token を取得して、Easy Auth で保護されている Web API が呼び出されます。以下は実行した例ですが API の呼び出しまで成功しています。

これで Service Principal を使って Access Token を取得し、Easy Auth で保護されている Web API を正しく呼び出せていることが確認出来ました。割と応用が利くので覚えておいて損はないはずです。

補足 : MSAL を利用せずに Access Token を取得する

今回は Access Token の取得に MSAL を使いましたが、Client Credentials Flow は token エンドポイントに POST リクエストを投げるだけなので、MSAL をわざわざ使う必要はありません。以下のドキュメントにもあるように、シンプルなリクエストを投げれば取得可能です。

重要なのは Web API 側でスコープを公開すること、Service Principal でスコープへの権限を追加すること、そして Access Token の取得時にスコープを指定することになります。

補足 : Easy Auth を使って Access Token を取得する

MSAL や HTTP リクエストを自分で叩いて Access Token を取得する以外にも、Easy Auth を使っている Web アプリケーションの場合は Azure Resource Explorer などでスコープを追加すればログイン時に必要な Access Token を同時に取得できます。

ちなみに Easy Auth で保護された Web App から同じく Easy Auth で保護された Web API を呼び出すサンプルは、珍しく公式ドキュメントにまとめられていますのでこちらを参考にするとよいです。

とはいえ考え方は Service Principal を利用する場合とほぼ同じで、ログイン時にアクセスしたい Web API のスコープを指定して Access Token を要求するだけです。スコープの指定方法だけが少し面倒ですが、コードを書く必要なく設定だけで済むためとても簡単です。

追記 : Easy Auth を使って Access Token を取得する場合にはユーザーがログインする側のアプリケーションに対して Web API のスコープを許可する必要があります。