ASP.NET Coreでのポリシー ベースの承認

ASP.NET Core承認ポリシーは、ユーザーがリソースへのアクセスを許可されているかどうかを判断するためにフレームワークが評価する 1 つ以上の承認要件の名前付きセットです。

この記事では、以下について説明しています。

  • 要件を作成する方法。
  • ポリシーを登録して適用する方法。
  • 単一および複数の要件評価の承認ハンドラー。
  • 1 つのポリシー内の複数の要件を評価する方法。

実際には、ポリシーは [Authorize(Policy = "...")] (Razor コンポーネント、ページ、コントローラー) または RequireAuthorization(...) (エンドポイント) と共に適用され、フレームワークはハンドラーを使用してポリシーの背後にある要件を評価します。 IAuthorizationPolicyProvider(ASP.NET Core ドキュメントのカスタム承認ポリシー プロバイダー) は、アプリの起動時に登録するのではなく、ポリシーを動的に生成します。

ロールベースの承認要求ベースの承認 では、要件、要件ハンドラー、および構成済みの承認ポリシーが使用されます。 これらの構成要素では、コードでの認可評価の式がサポートされています。

この記事では、Razor コンポーネントの例を使用し、ASP.NET Core 3.1 以降の Blazor 承認シナリオに焦点を当てます。 ASP.NET Coreのすべてのリリースに適用される Razor Pages と MVC のガイダンスについては、この記事を読んだ後の次のリソースを参照してください。

この記事の例 (ASP.NET Core 8.0 以降) では、C# 12 (.NET 8) 以降で使用可能なプライマリ コンストラクターを使用します。 詳細については、クラスと構造体のプライマリ コンストラクターの宣言 (C# ドキュメント チュートリアル) とプライマリ コンストラクター (C# ガイド) を参照してください。

要件とポリシーの登録

承認ポリシーは、現在のユーザー プリンシパルの承認を評価するためにポリシーによって使用される 1 つ以上の 要件で構成されます。 要件は、空のマーカー インターフェイスである IAuthorizationRequirement を実装します。

要件にデータが含まれていない場合、またはプロパティ (パラメーター) が含まれていない場合は、承認を処理するために関連付けられた 承認ハンドラー (IAuthorizationHandler) をトリガーするための空のマーカーとして機能します (この記事の後半で詳しく説明します)。 この場合のハンドラーは、HTTP コンテキスト、ユーザー要求、またはバックエンド データに完全に依存して、要件を満たすユーザーに関する決定を行うため、要件クラス自体には内部データやパラメーターは必要ありません。 この要件は、評価するルールをフレームワークに指示するだけです。

たとえば、マーカー クラスとして実装される次の最小年齢要件 (MinimumAgeRequirement) について考えてみます。

public class MinimumAgeRequirement : IAuthorizationRequirement { }

上記の要件は、ハンドラーがチェックする特定の期間を超えているユーザーを確認するポリシーを作成するために使用されます。 AuthorizationHandler<MinimumAgeRequirement>は、AuthorizationHandlerContext.Userを検査します。 ユーザーが特定の年齢を超えていることを示す生年月日要求がある場合、要件は成功します。 この場合、要件オブジェクトにはプロパティ (パラメーター) は必要ありません。 次の例では、最小年齢を設定するパラメーターを持つ最小年齢要件の完全な実装を示します。

次の MinimumAgeRequirement 要件を検討してください。この要件では、ユーザーの承認を評価するための 1 つのパラメーター (最小有効期間) について説明します。

using Microsoft.AspNetCore.Authorization;

namespace BlazorWebAppAuthorization.Policies.Requirements;

public class MinimumAgeRequirement(int minimumAge) : IAuthorizationRequirement
{
    public int MinimumAge { get; } = minimumAge;
}
using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int minimumAge) =>
        MinimumAge = minimumAge;

    public int MinimumAge { get; }
}
using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

ポリシーは、Programを呼び出すことによって、アプリのAuthorizationBuilder.AddPolicy ファイルの承認サービス構成の一部として登録されます。 次の例では、最小年齢の単一要件を持つ AtLeast21 ポリシーを作成し、最小年齢を 21 歳に設定します。

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AtLeast21", policy => 
        policy.Requirements.Add(new MinimumAgeRequirement(21)));

ポリシーは、Programを呼び出すことによって、アプリのAuthorizationBuilder.AddPolicy ファイルの承認サービス構成の一部として登録されます。 次の例では、最小年齢の 1 つの要件を持つ AtLeast21 ポリシーを作成し、最小年齢を 21 歳に設定します。

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

ポリシーは、Startup.ConfigureServicesを呼び出すことによって、Startup.cs (AuthorizationBuilder.AddPolicy) の承認サービス構成の一部として登録されます。 次の例では、最小年齢の 1 つの要件を持つ AtLeast21 ポリシーを作成し、最小年齢を 21 歳に設定します。

services.AddAuthorization(options =>
{
    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

承認ポリシーに複数の承認要件が含まれている場合、ポリシーの評価が成功するためには、すべての要件が合格する必要があります。 つまり、1 つの認可ポリシーに追加された複数の認可要件は、AND ベースで処理されます。

Razor コンポーネントにポリシーを適用する

Razor属性とポリシー名を使用して、[Authorize] コンポーネントにポリシーを適用します。

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]

複数のポリシーが適用されている場合は、アクセスが許可される前 にすべての ポリシーを通過する必要があります。

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]
@attribute [Authorize(Policy = "HumanResourcesMember")]

エンドポイントにポリシーを適用する

エンドポイントにポリシーを適用するには、RequireAuthorization を使用してポリシー名を指定します。 例えば次が挙げられます。

app.MapGet("/helloworld", () => "Hello World!")
    .RequireAuthorization("AtLeast21");

MVC アプリと Razor Pages アプリでポリシーを適用する

Razor Pages および MVC アプリでのポリシーの適用に関するガイダンスについては、次のリソースを参照してください。

承認サービス インターフェイス (IAuthorizationService)

IAuthorizationService は主に、 IAuthorizationService.AuthorizeAsync オーバーロードが呼び出されたときに承認が成功したかどうかを判断する役割を担います。

  • AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements): ユーザーが指定されたリソースの特定の承認要件のセットを満たしているかどうかを確認します。
  • AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName): ユーザーが指定されたリソースの特定の承認ポリシーを満たしているかどうかを確認します。

ポリシーの評価にリソースが必要ない場合は、リソースに対して null が渡されます。

上記のメソッドは、AuthorizationResultでラップされたTaskを返します。

IAuthorizationHandler は、 IAuthorizationHandler.HandleAsyncを介して要件が満たされているかどうかを確認する役割を担います。 AuthorizationHandlerContext クラスには、IAuthorizationHandler実装で使用される承認情報が含まれています。 IAuthorizationRequirement は、承認が成功したかどうかを追跡するためのメカニズムとして機能するメソッドのないマーカー インターフェイスです。 AuthorizationHandlerContext.SucceedIAuthorizationRequirementが呼び出されると、ポリシーが満たされます。

context.Succeed(requirement);

認可ハンドラー

認可ハンドラーでは、要件のプロパティの評価が行われます。 承認ハンドラーは、指定されたAuthorizationHandlerContextに対して要件を評価し、accessが許可されているかどうかを判断します。

1 つの要件に複数のハンドラーを指定できます。 ハンドラーは AuthorizationHandler<TRequirement>を継承できます。ここで、 TRequirement は処理する必要があります。 または、ハンドラーで IAuthorizationHandler を直接実装して、複数の種類の要件を処理することもできます。

1 つの要件にハンドラーを使用する

次の例では、最低年齢ハンドラーで 1 つの要件を処理する 1 対 1 の関係が示されています。

using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;

namespace BlazorWebAppAuthorization.Policies.Handlers;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
    {
        var dateOfBirthClaim = 
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);

        if (dateOfBirthClaim is null)
        {
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
        var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;

        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
    {
        var dateOfBirthClaim = 
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);

        if (dateOfBirthClaim is null)
        {
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
        var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;

        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
        MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
        {
            // Use the following if targeting a version of
            // .NET Framework older than 4.6:
            // return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);

        var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;

        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        // Use the following if targeting a version of
        // .NET Framework older than 4.6:
        // return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

上記のコードは、現在のユーザー プリンシパルに生年月日要求があるかどうかを判断します。 クレームが見つからないと認可は行われず、その場合は完了したタスクが返されます。 クレームが存在する場合は、ユーザーの年齢が計算されます。 要件で定義されている最低年齢をユーザーが満たしている場合、認可は成功と見なされます。 認可が成功すると、満たされた要件を唯一のパラメーターとして context.Succeed が呼び出されます。

複数の要件にハンドラーを使用する

次の例は、アクセス許可ハンドラーで 3 種類の異なる要件を処理できる一対多の関係を示したものです。

using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;

namespace BlazorWebAppAuthorization.Policies.Handlers;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource)
                    || IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission || requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        return Task.CompletedTask;
    }

    private static bool IsOwner(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }

    private static bool IsSponsor(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }
}
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                        requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        // Use the following if targeting a version of
        // .NET Framework older than 4.6:
        // return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

上のコードでは、成功とマークされていない要件が含まれる PendingRequirements プロパティが走査されます。 ReadPermission要件の場合、要求されたリソースをaccessするには、ユーザーが所有者またはスポンサーである必要があります。 EditPermission または DeletePermission の要件の場合、要求されたリソースにアクセスするための所有者である必要があります。

ハンドラーの登録

構成の間にサービス コレクションでハンドラーを登録します。 次の例では、最小有効期間ハンドラー (MinimumAgeHandler) をシングルトン サービスとして登録しますが、組み込みの サービス有効期間のいずれかを使用してハンドラーを登録できます。

builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

IAuthorizationRequirementIAuthorizationHandler の両方を実装する 1 つのクラスに、要件とハンドラーの両方をバンドルすることができます。 これにより、ハンドラーと要件の間に緊密な結合が作成されるので、単純な要件とハンドラーにのみ推奨されます。 両方のインターフェイスを実装するクラスを作成すると、要件で自身を処理できる組み込みの PassThroughAuthorizationHandler が原因で、サービス コンテナーにハンドラーを登録する必要がなくなります。

AssertionRequirementが要件であり、完全に自己完結型クラスのハンドラーである例については、ASP.NET Core クラスのAssertionRequirementを参照してください。 AssertionRequirement フレームワークの API を使用すると、個別の定型要件とハンドラー クラスを記述する代わりに、インラインラムダ式を使用してアクセスを検証できます。

Note

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

ハンドラーは何を返すべきか?

Handle メソッドは値を返しません。 成功または失敗の状態はどのようにして示されるのでしょうか。

  • ハンドラーは、正常に検証された要件 (context.Succeed) を渡して、IAuthorizationRequirementを呼び出すことによって成功を示します。

  • 同じ要件の他のハンドラーが成功する可能性があるため、一般的にエラーを処理するためにハンドラーは必要ありません。

  • 他の要件ハンドラーが成功した場合でも、失敗を保証するには、context.Fail を呼び出します。

あるハンドラーで context.Succeed または context.Fail が呼び出された場合でも、他のすべてのハンドラーが呼び出されます。 これにより、要件で別のハンドラーが正常に検証または失敗した場合でも発生する、ログ記録などの副作用を生成する要件が可能になります。 false に設定すると、InvokeHandlersAfterFailure プロパティにより、context.Fail が呼び出されたときのハンドラーの実行が省略されます。 InvokeHandlersAfterFailure の既定値は true で、すべてのハンドラーが呼び出されます。

Note

認可ハンドラーは、認証が失敗した場合でも呼び出されます。 また、ハンドラーは任意の順序で実行できるため、ハンドラーの呼び出し順序に依存 しません

要件に対して複数のハンドラーが必要な理由

評価を OR ベースで行いたい場合は、1 つの要件に対して複数のハンドラーを実装します。 たとえば、Contoso Corporation にはキー カードのみで開くドアがあるとします。 鍵カードを自宅に置いておくと、受付の方が一時ステッカーを印刷してドアを開けます。 このシナリオでは、アプリには 1 つの要件がありますが、それぞれが 1 つの要件を調べる複数のハンドラーがあります。

次の実装例では、

  • BuildingEntryRequirement は建物のエントリ要件です。
  • BadgeEntryHandler (個人はバッジを持っています)、 TemporaryStickerHandler (個人は一時的なステッカーを持っています)、それぞれが単一の要件を調べる別々のハンドラーです。

BuildingEntryRequirement.cs:

using Microsoft.AspNetCore.Authorization;

namespace BlazorWebAppAuthorization.Policies.Requirements;

public class BuildingEntryRequirement : IAuthorizationRequirement { }

BadgeEntryHandler.cs:

using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;

namespace BlazorWebAppAuthorization.Policies.Handlers;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId"))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

TemporaryStickerHandler.cs:

using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;

namespace BlazorWebAppAuthorization.Policies.Handlers;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => 
            c.Type == "TemporaryBadgeId" &&
            c.Issuer == "https://contososecurity"))
        {
            // Code to check expiration date omitted for brevity.
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

BuildingEntryRequirement.cs:

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

BadgeEntryHandler.cs:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                    BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => 
            c.Type == "BadgeId" &&
            c.Issuer == "https://contososecurity"))
        {
            context.Succeed(requirement);
        }

        // Use the following if targeting a version of
        // .NET Framework older than 4.6:
        // return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

TemporaryStickerHandler.cs:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
        BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => 
            c.Type == "TemporaryBadgeId" &&
            c.Issuer == "https://contososecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }

        // Use the following if targeting a version of
        // .NET Framework older than 4.6:
        // return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

両方のハンドラーを登録する必要があります。 ポリシーが BuildingEntryRequirementを評価したときにいずれかのハンドラーが成功した場合、ポリシーの評価は成功します。

Funcを使用してポリシーを満たす

Func<AuthorizationHandlerContext, bool> ポリシー ビルダーを使用してポリシーを構成するときに、RequireAssertion デリゲートを使用してコードで簡単にポリシーを表現できる場合があります。 たとえば、上記の BadgeEntryHandler は次のように書き換えることができます。

    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
            (c.Type == "BadgeId" || c.Type == "TemporaryBadgeId")
            && c.Issuer == "https://contososecurity")));
});

// <snippet_minimumAgeHandlerRegistration>
services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

グローバル ユーザー認証を要求する

すべてのアプリ ユーザーに認証を要求する方法については、「承認によって保護されたユーザー データを使用して ASP.NET Core アプリを作成するを参照してください。

外部サービス サンプルを使用した承認

外部サービスによる承認のサンプル (dotnet/AspNetCore.Docs.Samples GitHub リポジトリ) は、外部承認サービスで追加の承認要件を実装する方法を示しています。 ソリューションのContoso.API プロジェクトは、Microsoft Entra IDで保護されます。 Contoso.Security.API project からの追加の承認チェックでは、Contoso.API クライアント アプリが GetWeather API を呼び出すことができるかどうかを示すペイロードが返されます。

サンプルの構成

次のデモでは、コマンド シェルで NSwag (Swagger/OpenAPI) または cURL を使用します。

Contoso.Security.API プロジェクトで、AllowedClients プレースホルダー ({CLIENT ID}) を任意のテスト GUID 値 (たとえば、00001111-aaaa-2222-bbbb-3333cccc4444) に設定します。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AllowedClients": [
    "{CLIENT ID (FOR THE CLIENT CALLING CONTOSO.API)}"
  ]
}

Contoso.API プロジェクトに対して開かれたコマンド シェルで、dotnet user-jwtsを使用して、前の手順で作成したクライアント アプリの ID のappid要求を含むアクセス トークンを生成します (例: 00001111-aaaa-2222-bbbb-3333cccc4444)。

dotnet user-jwts create --claim appid={GUID}

Example:

dotnet user-jwts create --claim appid=00001111-aaaa-2222-bbbb-3333cccc4444

出力では、コマンド シェルの "Token:" の後にトークンが生成されます。

New JWT saved with ID '{JWT ID}'.
Name: {USER}
Custom Claims: [appid=00001111-aaaa-2222-bbbb-3333cccc4444]

Token: {TOKEN}

後で使用するために、トークンの値 (前の出力に {TOKEN} プレースホルダーが表示される場所) を別に設定します。

jwt.msなどのオンライン JWT デコーダーでトークンをデコードしてコンテンツを表示し、クライアント アプリの ID を持つappid要求が含まれていることを明らかにできます。

{
  "alg": "HS256",
  "typ": "JWT"
}.{
  "unique_name": "{USER}",
  "sub": "{USER}",
  "jti": "14ed7729",
  "appid": "{CLIENT ID}",
  "aud": [
    "https://localhost:7250",
    "http://localhost:7251"
  ],
  "nbf": 1780660887,
  "exp": 1788609687,
  "iat": 1780660888,
  "iss": "dotnet-user-jwts"
}.[Signature]

正しくないクライアント ID (appid) 値を指定してコマンドを再度実行します。

dotnet user-jwts create --claim appid=aaaabbbb-0000-cccc-1111-dddd2222eeee

2 番目のトークンの値を別に設定します。

Visual Studioまたはコマンド シェルの Contoso.API コマンドを使用して、Contoso.Security.API プロジェクトとdotnet watch プロジェクトの両方を開始します。

dotnet watch

Contoso.API プロジェクトの Swagger UI (https://localhost:7250/swagger/index.html) で、[承認] ボタンを選択します。

[ 使用可能な承認: Bearer ] ウィンドウで、アクセス トークンを入力します。 [承認] ボタンを選択します。 [使用可能な承認] ウィンドウを閉じます。

default で、 エンドポイントの /WeatherForecast ボタンを選択します。 [試してみる] ボタンを選択します。 [実行] ボタンを選択します。

Responses>Server response>応答本文の下の出力には、Contoso.API プロジェクトによって返された天気予報JSONが表示されます。

無効なクライアント アプリ ID で生成されたアクセス トークンで同じ手順を実行します。 応答は 403 - 許可されていません

その他のリソース