Azure App Service では、組み込みの認証機能が提供されています。この機能は Easy Auth と呼ばれていて、アプリケーション側に最低限のコードを実装するだけで、Azure AD などのさまざまなプロバイダーを使って保護できます。
docs.microsoft.com
今回は、Web Apps と Functions で Easy Auth を有効にして、Azure AD で認証されたフロントエンドの Web アプリケーションからバックエンドの API を呼び出します。
- Web Apps で ClaimsPrincipal を取得する
- Functions のアクセストークンを取得するように構成する
この2つの構成がポイントになるので、まとめておきます。
Azure App Service の Easy Auth を有効にする
Azure ポータルを使って、Web Apps と Functions の Easy Auth を有効にします。
認証プロバイダーに Azure AD を選択して設定する際に Express モードを選択すると、Azure AD のアプリケーションも同時に登録できます。
バックエンドの API をデプロイする
Functions には、Visual Studio 2019 のプロジェクトテンプレートで作成した Http Trigger の Functions をデプロイします。
Azure AD 認証するので、Functions の AuthorizationLevel は Anonymous
に変更しておきます。
[FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string name = req.Query["name"]; string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; // ClaimsPrincipal のユーザー名を取得 var identityName = req.HttpContext.User.Identity.Name; var responseMessage = string.IsNullOrEmpty(name) ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." : $"Hello, {name}. IdentityName is {identityName}."; return new OkObjectResult(responseMessage); }
Functions の場合、ClaimsPrincipal を自動的に HttpContext.User
に格納してくれますので、ユーザー名などを取得できます。
フロントエンドの Web アプリケーションをデプロイする
Web Apps には、Visual Studio 2019 のプロジェクトテンプレートで作成した ASP.NET Core 3.1 の MVC をデプロイします。
Easy Auth が設定してくれる HTTP ヘッダーの X-MS-TOKEN-AAD-ACCESS-TOKEN
にアクセストークンが格納されています。
docs.microsoft.com
このアクセストークンを使って、バックエンドの API を呼び出すコードを実装しておきます。
public async Task<IActionResult> Privacy() { var client = _clientFactory.CreateClient(); if (HttpContext.Request.Headers.ContainsKey("X-MS-TOKEN-AAD-ACCESS-TOKEN")) { var accessToken = HttpContext.Request.Headers["X-MS-TOKEN-AAD-ACCESS-TOKEN"]; client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } var response = await client.GetAsync($"https://gooner-api.azurewebsites.net/api/Function1?name=gooner"); if (response.StatusCode == HttpStatusCode.OK) { ViewData["ApiResult"] = await response.Content.ReadAsStringAsync(); } else { ViewData["ApiResult"] = $"Invalid status code in the HttpResponseMessage: {response.StatusCode}."; } return View(); }
ログインしたユーザー名とサインアウトをヘッダーに追加しておきます。Views/Sharedディレクトリに _LoginPartial.cshtml を追加し、_Layout.cshtml に組み込みます。
Easy Auth では、サインアウトのエンドポイントに /.auth/logout
を使います。
@using System.Security.Principal <ul class="navbar-nav"> @if (User.Identity.IsAuthenticated) { <li class="nav-item"> <span class="navbar-text text-dark">Hello @User.Identity.Name!</span> </li> <li class="nav-item"> <a class="nav-link text-dark" href="/.auth/logout">Sign out</a> </li> } </ul>
Web Apps の場合、ClaimsPrincipal が自動的に HttpContext.User
に格納されません。従来の ASP.NET の場合は、ClaimsPrincipal が自動的にバインドされていました。
ASP.NET Core の場合、アプリケーション側での対応が必要となります。
Web Apps で ClaimsPrincipal を取得する
前述の通り、ASP.NET Core では ClaimsPrincipal を取得するために、アプリケーション側での対応が必要となります。
docs.microsoft.com
HTTP ヘッダー(X-MS-CLIENT-PRINCIPAL-NAMEなど)を解析するか、/.auth/me
エンドポイントから取得するか、どちらかの方法になります。
これらの機能を実装する ASP.NET Core 向けの認証ミドルウェアは公式には提供されていませんが、一時的な回避方法として MaximeRouiller.Azure.AppService.EasyAuth
が提供されているので、今回はこちらを使います。
github.com
認証ミドルウェアの NuGet パッケージをインストールします。
PM>Install-Package MaximeRouiller.Azure.AppService.EasyAuth -Version 1.0.0-ci141
Startup クラスで、ミドルウェアパイプラインに Easy Auth の ClaimsPrincipal を取得する認証ミドルウェアを追加します。
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient(); // Easy Auth の ClaimsPrincipal を取得する認証ミドルウェア services.AddAuthentication().AddEasyAuthAuthentication((configure) => {}); services.AddControllersWithViews(); }
Functions のアクセストークンを取得するように構成する
HTTP ヘッダーの X-MS-TOKEN-AAD-ACCESS-TOKEN
にアクセストークンが格納されていますが、このままでは Functions の認証が通りません。
docs.microsoft.com
フロントエンドの Web アプリケーションを認証する際に、バックエンドの API を呼び出すトークンも含めるように構成する必要があります。
Azure ポータルから構成できないので、Azure Resource Explorer を使います。
Azure Resource Explorer から Web Apps の authsettings で、additionalLoginParams
を構成します。
"additionalLoginParams": [ "response_type=code id_token", "resource=<Azure AD に登録された Functions の Application (client) ID>" ],
実行結果
Azure AD で認証されたフロントエンドの Web アプリケーションから、バックエンドの API を呼び出せることを確認します。
Web Apps と Functions で ClaimsPrincipal を取得できました。
まとめ
Web Apps と Functions で Easy Auth を有効にして、Azure AD で認証されたフロントエンドの Web アプリケーションからバックエンドの API を呼び出しました。
アプリケーション側で認証機能を実装せずとも、要件を満たすのであれば、Easy Auth をうまく活用する選択肢もありだと思います。