ASP.NET Core 3.1 MVC による Microsoft アカウント認証のロール ベース アクセス制御

# はじめに

前回、以下の記事で、ASP.NET Core 3.1 MVC による Microsoft アカウント認証の実装手順を説明しました。

- [ASP.NET Core 3.1 MVC による Microsoft アカウント認証](https://qiita.com/hiromasa-masuda/items/e34bc7c52eaa943126d3)

認証ができたら、次は、ロール ベース アクセス コントロール、つまり、コントローラーやアクションに、[Authorize(Roles="admin")] みたいな属性をつけて、特定のグループに所属している場合のみにアクセスを許可するといったことをやりたいのですが、Microsoft アカウント認証のみを対象としたときは、やり方が見つかりませんでした。
Azure Active Directory アカウントでは、以下の方法で、roles や groups をクレームに含めて、アカウントのロールとして管理、制御できます。


- [How to: Add app roles in your application and receive them in the token](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps)
- [Add authorization using groups & group claims to an ASP.NET Core Web app that signs-in users with the Microsoft identity platform](https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/5-WebApp-AuthZ/5-2-Groups)

これらの方法の様に所属しているロールをクレームに含めることができれば、ロールの管理が容易となるのですが、今回は、アプリ側で、ロールを定義して ASP.NET Core のポリシーで認可を制御する方法を説明します。

# ロールの定義

今回は、appSettings.json にロールと所属するアカウントを定義していきます。
ここでは、Admins を配列として定義して、その値に、所属するアカウントとして、対象アカウントのオブジェクト ID "http://schemas.microsoft.com/identity/claims/objectidentifier" の値を定義しています。

```json:appSettings.json

{
  "Admins": [
    "00000000-0000-0000-0000-000000000000",
    "00000000-0000-0000-0000-000000000000",
    "00000000-0000-0000-0000-000000000000"
  ]
}

```
"http://schemas.microsoft.com/identity/claims/objectidentifier" の値は、実際に Microsoft アカウントを認証させて、確認しました。
以下の記事の手順で、Microsoft アカウント認証を行い、対象アカウントのオブジェクト ID を取得して、設定を行ってください。

[ASP.NET Core 3.1 MVC による Microsoft アカウント認証#動作確認](https://qiita.com/hiromasa-masuda/items/e34bc7c52eaa943126d3#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D)
http://schemas.microsoft.com/identity/claims/objectidentifier

# ポリシー ベースでの認可

Startup.ConfigureServices でポリシーを定義、追加します。
以下のコードでは、先ほど定義した appSettings.json の Admins 配列にオブジェクト ID が含まれているかを評価するポリシー "AdminOnly" を定義、追加しています。
AuthorizationPolicyBuilder.RequireAssertion を使うとクロージャーで簡単にポリシーを定義できます。

```c#:Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd");
    services.AddControllersWithViews().AddMicrosoftIdentityUI();

    services.Configure<AuthorizationOptions>(options =>
    {
        options.AddPolicy("AdminOnly", policy =>
            policy.RequireAssertion(context =>
            {
                if (!context.User.Identity.IsAuthenticated)
                {
                    return false;
                }
                // オブジェクト ID の取得
                var oid = context.User.Claims.FirstOrDefault(claim => claim.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");
                // appSettings.json の Admins にオブジェクト ID が含まれているか評価
                return this.Configuration.GetSection("Admins").Get<string[]>().Contains(oid?.Value);
            }));
    });
}


```

# コントローラーへのポリシーの設定

あとは、コントローラーやアクションに [Authorize] 属性とともに、ポリシーを指定します。
[Authorize(Policy = "ポリシー名")] でポリシー名を指定します。
AuthorizeAttribute の[コンストラクター](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute.-ctor?view=aspnetcore-3.1#Microsoft_AspNetCore_Authorization_AuthorizeAttribute__ctor_System_String_)で、ポリシー名を指定できるので、[Authorize("ポリシー名")] とすることもできます。

```csharp:HomeController.cs

[Authorize(Policy = "AdminOnly")]
public IActionResult Edit()
{
    return View();
}

[Authorize("AdminOnly")]
public IActionResult Delete()
{
    return View();
}

```

# まとめ

[ASP.NET Core 3.1 MVC による Microsoft アカウント認証](https://qiita.com/hiromasa-masuda/items/e34bc7c52eaa943126d3) の手順で、Azure Active Directory にアプリを登録する際に、[サポートされているアカウントの種類] を "個人用 Microsoft アカウントのみ" とした時、つまり、Microsoft アカウント認証を選択した場合は、ロールの定義や定義したロールをクレームに含めることができないようです。
よって、今回は、ロールの定義をアプリ側で行い、ユーザー アカウントがロールに含まれていることを検証するポリシーを定義し、ポリシー ベースの認可で、ロール ベース ライクなアクセス制御を実現してみました。
実は、ASP.NET Core のミドルウェアで、クレームを追加して [Authorize(Roles="ロール名")] といったこともできるのですが、セキュリティ面でどうなのかという点と、ポリシー ベースの方が、色々と応用が利くと考えたので、今回は、ポリシー ベースでの説明としました。

# 参考サイト

- [Policy-based authorization in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies)
- [Role-based authorization in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles)
- [Claims-based authorization in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims)
- [Microsoft identity platform ID tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens)