保護された Web API: スコープとアプリのロールを検証する

この記事では、Web API に承認を追加する方法について説明します。 この保護により、API を呼び出せるのは以下のものだけになります。

  • 適切なスコープとロールを持つユーザーに代わるアプリケーション。
  • 適切なアプリケーション ロールを持つデーモン アプリ。

この記事のコード スニペットは、GitHub の以下のコード サンプルから抜粋されたものです。

ASP.NET または ASP.NET Core の Web API を保護するには、次のいずれかの項目に [Authorize] 属性を追加する必要があります。

  • すべてのコントローラー アクションを保護する場合は、コントローラー自体
  • API の個々のコントローラー アクション
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

ただし、この保護は不十分です。 ASP.NET および ASP.NET Core でトークンが検証されることしか保証されません。 API は、API の呼び出しに使用されるトークンが、予期された要求を使用して要求されていることを検証する必要があります。 特に次の要求では検証が必要です。

  • API がユーザーの代わりに呼び出される場合は、"スコープ"。
  • API をデーモン アプリから呼び出すことができる場合は、"アプリ ロール"。

ユーザーに代わって呼び出される API のスコープの確認

クライアント アプリがユーザーに代わって API を呼び出す場合、API は、API 用の特定のスコープを持つベアラー トークンを要求する必要があります。 詳細については、「コード構成」の「ベアラー トークン」を参照してください。

ASP.NET Core では、各コントローラー アクションのスコープを検証するために、Microsoft.Identity.Web を使用できます。 コントローラーのレベルまたはアプリケーション全体で検証することもできます。

各コントローラー アクションのスコープを確認する

[RequiredScope] 属性を使用して、コントローラー アクション内のスコープを検証できます。 この属性にはいくつかのオーバーライドがあります。 必要なスコープを直接取得するものと、構成に対するキーを取得するものです。

ハードコードされたスコープでコントローラー アクションのスコープを検証する

次のコード スニペットは、ハードコードされたスコープでの [RequiredScope] 属性の使用方法を示しています。

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
構成で定義されたスコープを使用して、コントローラー アクションのスコープを検証する

構成でこれらの必要なスコープを宣言し、構成キーを参照することもできます。

たとえば、appsettings.json に次のような構成があるとします。

{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

この場合、[RequiredScope] 属性でこれを参照します。

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
スコープを条件付きで検証する

スコープを条件付きで検証したい場合があります。 これは、HttpContextVerifyUserHasAnyAcceptedScope 拡張メソッドを使用して行います。

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

コントローラーのレベルでスコープを検証する

コントローラー全体のスコープを検証することもできます

ハードコードされたスコープでコントローラーのスコープを検証する

次のコード スニペットは、コントローラーのハードコードされたスコープでの [RequiredScope] 属性の使用方法を示しています。 RequiredScopeAttribute を使用するには、次のいずれかが必要になります。

  • Startup.csAddMicrosoftIdentityWebApi を、コード構成で示すように使用する
  • あるいは、承認ポリシーで説明しているように ScopeAuthorizationRequirement を承認ポリシーに追加する
using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
構成で定義されたスコープを使用して、コントローラーのスコープを検証する

アクションと同様に、構成でこれらの必要なスコープを宣言し、構成キーを参照することもできます。

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

スコープをよりグローバルに検証する

Web API の詳細なスコープを定義し、各コントローラー アクションでスコープを確認することが、お勧めの方法です。 ただし、アプリケーションまたはコントローラーのレベルでスコープを検証することもできます。 詳細については、ASP.NET のコア ドキュメントの「ASP.NET Core でのクレーム ベースの承認」を参照してください。

検証される対象

[RequiredScope] 属性と VerifyUserHasAnyAcceptedScope メソッドは、次の手順のような処理を行います。

  • http://schemas.microsoft.com/identity/claims/scope または scp という名前の要求があることを確認します。
  • 要求が、API で想定されているスコープを含む値を持っていることを確認します。

デーモン アプリによって呼び出される API のアプリ ロールの確認

Web API が デーモン アプリによって呼び出された場合、そのアプリには Web API に対するアプリケーションのアクセス許可が必要です。 「アプリケーションのアクセス許可 (アプリ ロール) の公開」に示されているように、API はそのようなアクセス許可を公開します。 1 つの例として、access_as_application アプリ ロールがあります。

ここでは、受け取ったトークンに roles 要求が含まれていることと、この要求に想定されている値が含まれていることを API に確認させる必要があります。 この確認コードは、委任されたアクセス許可を確認するコードと似ています。異なるのは、コントローラー アクションでテストされるのが、スコープではなくロールである点です。

次のコード スニペットは、アプリケーション ロールを検証する方法を示しています

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

代わりに、コントローラーまたはアクション (または razor ページ) で [[Authorize(Roles = "access_as_application")]] 属性を使用できます。

[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

ASP.NET Core のロールベースの承認には、ロールベースの承認を実装するためのアプローチがいくつかリストされています。 開発者は、それぞれのシナリオに応じて、いずれかのアプローチを選択できます。

実際のサンプルについては、ロールとグループによる認可に関する Web アプリの増分チュートリアルを参照してください。

ユーザーに代わって呼び出される API のアプリ ロールの確認

ユーザーは、「方法: アプリケーションにアプリ ロールを追加してトークンで受け取る」に示されているように、ユーザー割り当てパターンでロール要求を使用することもできます。 ロールが両方に割り当て可能な場合は、ロールをチェックすると、アプリはユーザーとして、ユーザーはアプリとしてサインインできるようになります。 この混乱を避けるために、ユーザー用とアプリ用に異なるロールを宣言することをお勧めします。

ユーザー / グループを使用してアプリ ロールを定義している場合は、ロール要求をスコープと共に API で検証することもできます。 ユーザー / グループとアプリケーションのロール要求に違いはないため、このシナリオのアプリ ロールの検証ロジックは、API がデーモン アプリによって呼び出される場合と同じままです。

Web API がデーモン アプリのみによって呼び出される必要がある場合のアプリ専用トークンの受け入れ

デーモン アプリのみが Web API を呼び出せるようにしたい場合は、アプリ ロールを検証するときに、トークンがアプリ専用トークンであるという条件を追加します。

string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

逆の条件をチェックすると、ユーザーとしてサインインするアプリのみが API を呼び出すことができます。

ACL ベースの認可の使用

アプリロール ベースの認可の代わりに、roles 要求のないトークンを制御するためのアクセス制御リスト (ACL) ベースの認可パターンで Web API を保護することができます。

ASP.NET Core で Microsoft.Identity.Web を使用している場合は、ACL ベースの認可を使用していることを宣言する必要があります。そうしないと、指定された要求にロールもスコープも含まれない場合、Microsoft Identity Web は例外をスローします。

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

この例外を回避するには、appsettings.jsonAllowWebApiToBeAuthorizedByACL 構成プロパティをプログラムで true に設定します。

{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

AllowWebApiToBeAuthorizedByACLtrue に設定した場合、ACL メカニズムを確保するのは自分の責任になります。

次のステップ