Azure App Service の 認証/承認(EasyAuth) 設定で ClaimPrincipal を使えるようにする

EasyAuth という名でも知られる Azure App Service の 認証/承認（Authentication/Authorization）機能の構築はとても簡単です。認証プロバイダーとしてAzureADやFacebook, Twitter, Google, Apple が使えます。それ以外の認証プロバイダーも OpenIdConnect に対応していればすぐに設定できます。

[Azure App Service および Azure Functions での認証と承認](https://docs.microsoft.com/ja-jp/azure/app-service/overview-authentication-authorization)

AuthN/AuthZ(Authentication/Authorization)は認証を設定するのは簡単ですが、認証プロバイダーからもらった情報の一部だけが Header に格納されています。正直使いにくいです。そんな時に ASP.NET Core ユーザーは使いやすい  ClaimsPrincipal に自動的に Claim 情報をに入れてくれればいいのに、と思うことでしょう。

しかし、既定では ClaimsPrincipal (プロパティでいうと ControllerBase.User）に認証プロバイダーから得られる Claim はセットされません。
ここでは Microsoft.Identity.Web を使う方法をご紹介します。Microsoft.Identity.Web そのものは ASP.NET (Core）に認証機能を実装するための便利なライブラリで、App Service とは何の関係もありません。しかし ver1.2.0 以降から App Service の AuthN/AuthZ が設定されていることを確認すると Claim 情報を ClaimsPrincipal にセットしてくれるようになりました。これを利用します。

# 環境
Visual Studio 2022
ASP.NET Core MVC
.NET 6
AzureADを使った AuthN/AuthZを設定済み

以降、ASP.NET Core MVCに対する実装についての説明です。

# Microsoft.Identity.Web を使う
まず Microsoft.Identity.Web を使うように設定します。
Nugetからインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/7841cfa2-e645-945c-aac7-5311399aae00.png)

次に Program.cs に次のように実装を2か所追加します。追加、となっているところ以外は既定のままです。

```csharp:Program.cs
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

// 追加
builder.Services
    .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

// 追加
app.UseAuthentication();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();
```

# appSettings.json の修正

Program.cs への追記でわかるとおり、"AzureAD"という設定が appSettings.json に必要です。Azure PortalなどでAuthN/AuthZ をセットアップする場合、Azure AD 内部に作成されるアプリについては考える必要がないのが素敵ではありますが、実際にはアプリが作成されていることは意識したほうがいいでしょう。
appSettings.json にはその作成されたアプリの情報（ClientId）を設定する必要があります。

次の設定は、必要最低限の実装です。AzureAd の箇所以外は既定のままです。
TenantId には Azure AD のテナント Id を、ClientId には AuthN/AuthZ が自動的に作成したアプリの ClientId をセットします。

```json:appSettings.json
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "72f988bf-86f1-41af-91ab-xxxxxxxxxxxx",
    "ClientId": "83adc094-1f32-4a79-add2-xxxxxxxxxxxx"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

```

# 確認

設定はこれだけです。思ったより簡単ですね。次のコードで確認します。

```csharp:Controllers/HomeController.cs
        public IActionResult Index()
        {
            var claims = User.Claims.Select(x => $"type: {x.Type}, value: {x.Value}");
            base.ViewBag.Claims = claims;

            return View();
        }
```

```csharp:Views/Home/Index.cshtml
<div>Claims</div>
@{
    foreach(string claim in ViewBag.Claims)
    {
        <div>@claim</div>   
    }
}
```

実装が終わったら、AuthN/AuthZ設定済みの Azure App Service へデプロイします。当たり前ですがローカルでは認証設定していないので Claim 情報は何も表示されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/57d6363b-dc4c-98e6-1ed4-c312e82372e1.png)

塗りつぶしだらけですが、無事に Claim に値がセットされています。今回は AzureAD を使用している場合に限定してしまいましたが、他の認証プロバイダーを使用する場合については調べることがあったら追記するつもりです。

（追記）
特定の URL だけは認証したくない、という要件はよくあることだと思いますが、この場合の設定方法について別途投稿しています。

https://qiita.com/takashiuesaka/items/57db6c1600240408cd03

従来 URL ベースでの認証有無の設定は authorization.json を使う方法が紹介されていましたが、この方法は既に MS 公式ページでは紹介されていません。また Azure App Service on Linux ではサポートされていませんでした。それに代わる方法をご紹介しています。