From 6badf4537b019e0cf6917d617bf7d77050710c89 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 1 May 2026 11:24:02 -0400 Subject: [PATCH 1/7] Resource-based authorization article overhaul --- .openpublishing.redirection.json | 5 + .../security/authorization/resource-based.md | 187 ++++++++++ .../security/authorization/resource-based.md | 185 ++++++++++ aspnetcore/security/authentication/index.md | 6 +- .../security/authorization/resource-based.md | 247 ++++++++++++++ .../security/authorization/resourcebased.md | 319 ------------------ aspnetcore/security/authorization/views.md | 4 +- aspnetcore/signalr/authn-and-authz.md | 4 +- aspnetcore/toc.yml | 6 +- 9 files changed, 636 insertions(+), 327 deletions(-) create mode 100644 aspnetcore/mvc/security/authorization/resource-based.md create mode 100644 aspnetcore/razor-pages/security/authorization/resource-based.md create mode 100644 aspnetcore/security/authorization/resource-based.md delete mode 100644 aspnetcore/security/authorization/resourcebased.md diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 2bf9e708f555..5dd4f1a65a8d 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -1683,6 +1683,11 @@ "source_path": "aspnetcore/security/authorization/razor-pages-authorization.md", "redirect_url": "/aspnet/core/razor-pages/security/authorization/conventions", "redirect_document_id": false + }, + { + "source_path": "aspnetcore/security/authorization/resourcebased.md", + "redirect_url": "/aspnet/core/security/authorization/resource-based", + "redirect_document_id": false } ] } diff --git a/aspnetcore/mvc/security/authorization/resource-based.md b/aspnetcore/mvc/security/authorization/resource-based.md new file mode 100644 index 000000000000..3e2a8069344d --- /dev/null +++ b/aspnetcore/mvc/security/authorization/resource-based.md @@ -0,0 +1,187 @@ +--- +title: Resource-based authorization in ASP.NET Core MVC +ai-usage: ai-assisted +author: wadepickett +description: Learn how to implement resource-based authorization in an ASP.NET Core MVC app when an [Authorize] attribute doesn't suffice. +ms.author: wpickett +ms.custom: mvc +ms.date: 05/01/2026 +uid: mvc/security/authorization/resource-based +--- +# Resource-based authorization in ASP.NET Core MVC + +This article describes how to authorize users for access to app resources. + +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. + +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the action that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. + +[View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). + + contains a sample app that uses resource-based authorization. + +Examples in this article use *primary constructors*, available in C# 12 (.NET 8) or later. For more information, see [Declare primary constructors for classes and structs (C# documentation tutorial)](/dotnet/csharp/whats-new/tutorials/primary-constructors) and [Primary constructors (C# Guide)](/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors). Sample apps that accompany the article that target versions of .NET earlier than .NET 8 use constructor injection. + +## Use imperative authorization + +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to actions. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: + +```csharp +public class DocumentController(IAuthorizationService authorizationService, + IDocumentRepository documentRepository) : Controller +``` + + has two method overloads. One of the overloads accepts a resource and policy name: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + string policyName); +``` + +The other overload accepts a resource and list of requirements to evaluate: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + IEnumerable requirements); +``` + +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +```csharp +[HttpGet] +public async Task View(Guid documentId) +{ + Document document = documentRepository.Find(documentId); + + if (document == null) + { + return new NotFoundResult(); + } + + if ((await authorizationService + .AuthorizeAsync(User, document, Operations.Read)).Succeeded) + { + return View(document); + } + else + { + return new ChallengeResult(); + } +} +``` + + + +## Create a resource-based handler + +Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). + +The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs" id="snippet_HandlerAndRequirement"::: + + + +In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. + +:::moniker range=">= aspnetcore-6.0" + +Register the requirement and handler in `Program.cs`: + +```csharp +builder.Services.AddAuthorizationBuilder() + .AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); + +builder.Services.AddSingleton(); +``` + +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +Register the requirement and handler in `Startup.ConfigureServices`: + +```csharp +services.AddAuthorization(options => +{ + options.AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); +}); + +services.AddSingleton(); +``` + +:::moniker-end + +### Operational requirements + +To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_OperationsClass"::: + + + +The handler is implemented as follows, using an requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_Handler"::: + + + +The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. + +## Challenge and forbid with an operational resource handler + +This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. + +To call an operational resource handler, specify the operation when invoking in the action. The following example determines whether the authenticated user is permitted to view the provided document. + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +```csharp +if ((await authorizationService + .AuthorizeAsync(User, document, Operations.Read)).Succeeded) +{ + return View(document); +} +else +{ + return new ChallengeResult(); +} +``` + +If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. diff --git a/aspnetcore/razor-pages/security/authorization/resource-based.md b/aspnetcore/razor-pages/security/authorization/resource-based.md new file mode 100644 index 000000000000..b096a3370f18 --- /dev/null +++ b/aspnetcore/razor-pages/security/authorization/resource-based.md @@ -0,0 +1,185 @@ +--- +title: Resource-based authorization in ASP.NET Core Razor Pages +ai-usage: ai-assisted +author: wadepickett +description: Learn how to implement resource-based authorization in an ASP.NET Core Razor Pages app when an [Authorize] attribute doesn't suffice. +ms.author: wpickett +ms.custom: mvc +ms.date: 04/29/2026 +uid: razor-pages/security/authorization/resource-based +--- +# Resource-based authorization in ASP.NET Core Razor Pages + +This article describes how to authorize users for access to app resources. + +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. + +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the page handler that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. + +[View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). + + contains a sample app that uses resource-based authorization. + +Examples in this article use *primary constructors*, available in C# 12 (.NET 8) or later. For more information, see [Declare primary constructors for classes and structs (C# documentation tutorial)](/dotnet/csharp/whats-new/tutorials/primary-constructors) and [Primary constructors (C# Guide)](/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors). Sample apps that accompany the article that target versions of .NET earlier than .NET 8 use constructor injection. + +## Use imperative authorization + +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: + +```csharp +public class DocumentModel(IAuthorizationService authorizationService, + IDocumentRepository documentRepository) : PageModel +``` + + has two method overloads. One of the overloads accepts a resource and policy name: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + string policyName); +``` + +The other overload accepts a resource and list of requirements to evaluate: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + IEnumerable requirements); +``` + +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +```csharp +public async Task OnGetAsync(Guid documentId) +{ + Document = documentRepository.Find(documentId); + + if (Document == null) + { + return new NotFoundResult(); + } + + var authorizationResult = await authorizationService + .AuthorizeAsync(User, Document, "EditPolicy"); + + if (authorizationResult.Succeeded) + { + return Page(); + } + else if (User.Identity.IsAuthenticated) + { + return new ForbidResult(); + } + else + { + return new ChallengeResult(); + } +} +``` + + + +## Create a resource-based handler + +Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). + +The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs" id="snippet_HandlerAndRequirement"::: + + + +In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. + +:::moniker range=">= aspnetcore-6.0" + +Register the requirement and handler in `Program.cs`: + +```csharp +builder.Services.AddAuthorizationBuilder() + .AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); + +builder.Services.AddSingleton(); +``` + +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +Register the requirement and handler in `Startup.ConfigureServices`: + +```csharp +services.AddAuthorization(options => +{ + options.AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); +}); + +services.AddSingleton(); +``` + +:::moniker-end + +### Operational requirements + +To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_OperationsClass"::: + + + +The handler is implemented as follows, using an requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_Handler"::: + + + +The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. + +## Challenge and forbid with an operational resource handler + +This section shows how the challenge and forbid results are processed and how challenge and forbid differ. + +To call an operational resource handler, specify the operation when invoking in the page handler. The following example determines whether the authenticated user is permitted to view the provided document. + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +```csharp +var authorizationResult = await authorizationService + .AuthorizeAsync(User, Document, Operations.Read); +``` + +If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. diff --git a/aspnetcore/security/authentication/index.md b/aspnetcore/security/authentication/index.md index 80582833bda9..3c3c6be5a9f5 100644 --- a/aspnetcore/security/authentication/index.md +++ b/aspnetcore/security/authentication/index.md @@ -133,7 +133,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resourcebased#challenge-and-forbid-with-an-operational-resource-handler). +* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant @@ -281,7 +281,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resourcebased#challenge-and-forbid-with-an-operational-resource-handler). +* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant @@ -424,7 +424,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resourcebased#challenge-and-forbid-with-an-operational-resource-handler). +* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant diff --git a/aspnetcore/security/authorization/resource-based.md b/aspnetcore/security/authorization/resource-based.md new file mode 100644 index 000000000000..f21e73fe5753 --- /dev/null +++ b/aspnetcore/security/authorization/resource-based.md @@ -0,0 +1,247 @@ +--- +title: Resource-based authorization in ASP.NET Core +ai-usage: ai-assisted +author: wadepickett +description: Learn how to implement resource-based authorization in an ASP.NET Core app when an Authorize attribute won't suffice. +ms.author: wpickett +ms.custom: mvc +ms.date: 05/01/2026 +uid: security/authorization/resource-based +--- +# Resource-based authorization in ASP.NET Core + +This article describes how to authorize users for access to app resources. + +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. + +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the page handler or action that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. + +[View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). + + contains a sample app that uses resource-based authorization. + +Examples in this article use *primary constructors*, available in C# 12 (.NET 8) or later. For more information, see [Declare primary constructors for classes and structs (C# documentation tutorial)](/dotnet/csharp/whats-new/tutorials/primary-constructors) and [Primary constructors (C# Guide)](/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors). Sample apps that accompany the article that target versions of .NET earlier than .NET 8 use constructor injection. + +## Use imperative authorization + +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: + +FOR MVC ... + +```csharp +public class DocumentController(IAuthorizationService authorizationService, + IDocumentRepository documentRepository) : Controller +``` + +FOR RP ... + +```csharp +public class DocumentModel(IAuthorizationService authorizationService, + IDocumentRepository documentRepository) : PageModel +``` + + has two method overloads. One of the overloads accepts a resource and policy name: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + string policyName); +``` + +The other overload accepts a resource and list of requirements to evaluate: + +```csharp +Task AuthorizeAsync( + ClaimsPrincipal user, + object resource, + IEnumerable requirements); +``` + +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +FOR MVC ... + +```csharp +[HttpGet] +public async Task View(Guid documentId) +{ + Document document = documentRepository.Find(documentId); + + if (document == null) + { + return new NotFoundResult(); + } + + if ((await authorizationService + .AuthorizeAsync(User, document, Operations.Read)).Succeeded) + { + return View(document); + } + else + { + return new ChallengeResult(); + } +} +``` + + + +FOR RP ... + +```csharp +public async Task OnGetAsync(Guid documentId) +{ + Document = documentRepository.Find(documentId); + + if (Document == null) + { + return new NotFoundResult(); + } + + var authorizationResult = await authorizationService + .AuthorizeAsync(User, Document, "EditPolicy"); + + if (authorizationResult.Succeeded) + { + return Page(); + } + else if (User.Identity.IsAuthenticated) + { + return new ForbidResult(); + } + else + { + return new ChallengeResult(); + } +} +``` + + + +## Create a resource-based handler + +Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). + +The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs" id="snippet_HandlerAndRequirement"::: + + + +In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. + +:::moniker range=">= aspnetcore-6.0" + +Register the requirement and handler in `Program.cs`: + +```csharp +builder.Services.AddAuthorizationBuilder() + .AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); + +builder.Services.AddSingleton(); +``` + +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +Register the requirement and handler in `Startup.ConfigureServices`: + +```csharp +services.AddAuthorization(options => +{ + options.AddPolicy("EditPolicy", policy => + policy.Requirements.Add(new SameAuthorRequirement())); +}); + +services.AddSingleton(); +``` + +:::moniker-end + +### Operational requirements + +To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_OperationsClass"::: + + + +The handler is implemented as follows, using an requirement and a `Document` resource: + +:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_Handler"::: + + + +The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. + +## Challenge and forbid with an operational resource handler + +This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. + +To call an operational resource handler, specify the operation when invoking in the page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. + +> [!NOTE] +> The following example assumes successful authentication with the `User` property set. + +FOR MVC ... + +```csharp +if ((await authorizationService + .AuthorizeAsync(User, document, Operations.Read)).Succeeded) +{ + return View(document); +} +else +{ + return new ChallengeResult(); +} +``` + +FOR RP ... + +```csharp +var authorizationResult = await authorizationService + .AuthorizeAsync(User, Document, Operations.Read); +``` + +If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. diff --git a/aspnetcore/security/authorization/resourcebased.md b/aspnetcore/security/authorization/resourcebased.md deleted file mode 100644 index be2ab9f5bb59..000000000000 --- a/aspnetcore/security/authorization/resourcebased.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: Resource-based authorization in ASP.NET Core -author: wadepickett -description: Learn how to implement resource-based authorization in an ASP.NET Core app when an Authorize attribute won't suffice. -ai-usage: ai-assisted -ms.author: wpickett -ms.custom: mvc -ms.date: 01/30/2026 -uid: security/authorization/resourcebased ---- -# Resource-based authorization in ASP.NET Core - -:::moniker range=">= aspnetcore-7.0" - -Authorization approach depends on the resource. For example, only the author of a document is authorized to update the document. Consequently, the document must be retrieved from the data store before authorization evaluation can occur. - -Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice. Instead, you can invoke a custom authorization method—a style known as *imperative authorization*. - -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/security/authorization/resourcebased/samples/3_0) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). - -[Create an ASP.NET Core app with user data protected by authorization](xref:security/authorization/secure-data) contains a sample app that uses resource-based authorization. - -## Use imperative authorization - -Authorization is implemented as an service and is registered in the service collection at application startup. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Controllers/DocumentController.cs?name=snippet_IAuthServiceDI&highlight=6)] - -`IAuthorizationService` has two `AuthorizeAsync` method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate. - -```csharp -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - IEnumerable requirements); -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - string policyName); -``` - - - -In the following example, the resource to be secured is loaded into a custom `Document` object. An `AuthorizeAsync` overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "EditPolicy" authorization policy is factored into the decision. See [Custom policy-based authorization](xref:security/authorization/policies) for more on creating authorization policies. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/Edit.cshtml.cs?name=snippet_DocumentEditHandler)] - -## Write a resource-based handler - -Writing a handler for resource-based authorization isn't much different than [writing a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class, and implement a requirement handler class. For more information on creating a requirement class, see [Requirements](xref:security/authorization/policies#requirements). - -The handler class specifies both the requirement and resource type. For example, a handler utilizing a `SameAuthorRequirement` and a `Document` resource follows: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs?name=snippet_HandlerAndRequirement)] - -In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. - -Register the requirement and handler in `Program.cs`: - -[!code-csharp[](resourcebased/samples/7.x/ResourceBasedAuthApp2/Program.cs?name=snippet_ConfigureServicesSample&highlight=4-6,8)] - -### Operational requirements - -If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_OperationsClass)] - -The handler is implemented as follows, using an `OperationAuthorizationRequirement` requirement and a `Document` resource: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_Handler)] - -The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. - -## Challenge and forbid with an operational resource handler - -This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. - -To call an operational resource handler, specify the operation when invoking `AuthorizeAsync` in your page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/View.cshtml.cs?name=snippet_DocumentViewHandler&highlight=10-11)] - -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning `ForbidResult` informs any authentication middleware that authorization failed. A `ChallengeResult` is returned when authentication must be performed. For interactive browser clients, it may be appropriate to redirect the user to a login page. - -:::moniker-end - -:::moniker range="= aspnetcore-6.0" - -Authorization approach depends on the resource. For example, only the author of a document is authorized to update the document. Consequently, the document must be retrieved from the data store before authorization evaluation can occur. - -Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice. Instead, you can invoke a custom authorization method—a style known as *imperative authorization*. - -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/security/authorization/resourcebased/samples/3_0) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). - -[Create an ASP.NET Core app with user data protected by authorization](xref:security/authorization/secure-data) contains a sample app that uses resource-based authorization. - -## Use imperative authorization - -Authorization is implemented as an service and is registered in the service collection at application startup. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Controllers/DocumentController.cs?name=snippet_IAuthServiceDI&highlight=6)] - -`IAuthorizationService` has two `AuthorizeAsync` method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate. - -```csharp -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - IEnumerable requirements); -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - string policyName); -``` - - - -In the following example, the resource to be secured is loaded into a custom `Document` object. An `AuthorizeAsync` overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "EditPolicy" authorization policy is factored into the decision. See [Custom policy-based authorization](xref:security/authorization/policies) for more on creating authorization policies. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/Edit.cshtml.cs?name=snippet_DocumentEditHandler)] - -## Write a resource-based handler - -Writing a handler for resource-based authorization isn't much different than [writing a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class, and implement a requirement handler class. For more information on creating a requirement class, see [Requirements](xref:security/authorization/policies#requirements). - -The handler class specifies both the requirement and resource type. For example, a handler utilizing a `SameAuthorRequirement` and a `Document` resource follows: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs?name=snippet_HandlerAndRequirement)] - -In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. - -Register the requirement and handler in `Program.cs`: - -[!code-csharp[](resourcebased/samples/6_0/ResourceBasedAuthApp2/Program.cs?name=snippet_ConfigureServicesSample&highlight=4-8,10)] - -### Operational requirements - -If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_OperationsClass)] - -The handler is implemented as follows, using an `OperationAuthorizationRequirement` requirement and a `Document` resource: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_Handler)] - -The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. - -## Challenge and forbid with an operational resource handler - -This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. - -To call an operational resource handler, specify the operation when invoking `AuthorizeAsync` in your page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/View.cshtml.cs?name=snippet_DocumentViewHandler&highlight=10-11)] - -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning `ForbidResult` informs any authentication middleware that authorization failed. A `ChallengeResult` is returned when authentication must be performed. For interactive browser clients, it may be appropriate to redirect the user to a login page. - -:::moniker-end - -:::moniker range=">= aspnetcore-3.0 < aspnetcore-6.0" - -Authorization approach depends on the resource. For example, only the author of a document is authorized to update the document. Consequently, the document must be retrieved from the data store before authorization evaluation can occur. - -Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice. Instead, you can invoke a custom authorization method—a style known as *imperative authorization*. - -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/security/authorization/resourcebased/samples/3_0) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). - -[Create an ASP.NET Core app with user data protected by authorization](xref:security/authorization/secure-data) contains a sample app that uses resource-based authorization. - -## Use imperative authorization - -Authorization is implemented as an service and is registered in the service collection within the `Startup` class. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Controllers/DocumentController.cs?name=snippet_IAuthServiceDI&highlight=6)] - -`IAuthorizationService` has two `AuthorizeAsync` method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate. - -```csharp -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - IEnumerable requirements); -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - string policyName); -``` - - - -In the following example, the resource to be secured is loaded into a custom `Document` object. An `AuthorizeAsync` overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "EditPolicy" authorization policy is factored into the decision. See [Custom policy-based authorization](xref:security/authorization/policies) for more on creating authorization policies. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/Edit.cshtml.cs?name=snippet_DocumentEditHandler)] - -## Write a resource-based handler - -Writing a handler for resource-based authorization isn't much different than [writing a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class, and implement a requirement handler class. For more information on creating a requirement class, see [Requirements](xref:security/authorization/policies#requirements). - -The handler class specifies both the requirement and resource type. For example, a handler utilizing a `SameAuthorRequirement` and a `Document` resource follows: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs?name=snippet_HandlerAndRequirement)] - -In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. - -Register the requirement and handler in `Startup.ConfigureServices`: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Startup.cs?name=snippet_ConfigureServicesSample&highlight=4-8,10)] - -### Operational requirements - -If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_OperationsClass)] - -The handler is implemented as follows, using an `OperationAuthorizationRequirement` requirement and a `Document` resource: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_Handler)] - -The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. - -## Challenge and forbid with an operational resource handler - -This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. - -To call an operational resource handler, specify the operation when invoking `AuthorizeAsync` in your page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/View.cshtml.cs?name=snippet_DocumentViewHandler&highlight=10-11)] - -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning `ForbidResult` informs any authentication middleware that authorization failed. A `ChallengeResult` is returned when authentication must be performed. For interactive browser clients, it may be appropriate to redirect the user to a login page. - -:::moniker-end - -:::moniker range="< aspnetcore-3.0" - -Authorization approach depends on the resource. For example, only the author of a document is authorized to update the document. Consequently, the document must be retrieved from the data store before authorization evaluation can occur. - -Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice. Instead, you can invoke a custom authorization method—a style known as *imperative authorization*. - -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/security/authorization/resourcebased/samples/2_2) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). - -[Create an ASP.NET Core app with user data protected by authorization](xref:security/authorization/secure-data) contains a sample app that uses resource-based authorization. - -## Use imperative authorization - -Authorization is implemented as an service and is registered in the service collection within the `Startup` class. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Controllers/DocumentController.cs?name=snippet_IAuthServiceDI&highlight=6)] - -`IAuthorizationService` has two `AuthorizeAsync` method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate. - -```csharp -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - IEnumerable requirements); -Task AuthorizeAsync(ClaimsPrincipal user, - object resource, - string policyName); -``` - - - -In the following example, the resource to be secured is loaded into a custom `Document` object. An `AuthorizeAsync` overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "EditPolicy" authorization policy is factored into the decision. See [Custom policy-based authorization](xref:security/authorization/policies) for more on creating authorization policies. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/Edit.cshtml.cs?name=snippet_DocumentEditHandler)] - -## Write a resource-based handler - -Writing a handler for resource-based authorization isn't much different than [writing a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class, and implement a requirement handler class. For more information on creating a requirement class, see [Requirements](xref:security/authorization/policies#requirements). - -The handler class specifies both the requirement and resource type. For example, a handler utilizing a `SameAuthorRequirement` and a `Document` resource follows: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs?name=snippet_HandlerAndRequirement)] - -In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. - -Register the requirement and handler in `Startup.ConfigureServices`: - -[!code-csharp[](resourcebased/samples/2_2/ResourceBasedAuthApp2/Startup.cs?name=snippet_ConfigureServicesSample&highlight=3-7,9)] - -### Operational requirements - -If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_OperationsClass)] - -The handler is implemented as follows, using an `OperationAuthorizationRequirement` requirement and a `Document` resource: - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs?name=snippet_Handler)] - -The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. - -## Challenge and forbid with an operational resource handler - -This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. - -To call an operational resource handler, specify the operation when invoking `AuthorizeAsync` in your page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. - -> [!NOTE] -> The following code samples assume authentication has run and set the `User` property. - -[!code-csharp[](resourcebased/samples/3_0/ResourceBasedAuthApp2/Pages/Document/View.cshtml.cs?name=snippet_DocumentViewHandler&highlight=10-11)] - -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning `ForbidResult` informs any authentication middleware that authorization failed. A `ChallengeResult` is returned when authentication must be performed. For interactive browser clients, it may be appropriate to redirect the user to a login page. - -:::moniker-end diff --git a/aspnetcore/security/authorization/views.md b/aspnetcore/security/authorization/views.md index 10c8331fc0fd..b4ae52110693 100644 --- a/aspnetcore/security/authorization/views.md +++ b/aspnetcore/security/authorization/views.md @@ -18,7 +18,7 @@ A developer often wants to show, hide, or otherwise modify a UI based on the cur If you want the authorization service in every view, place the `@inject` directive into the `_ViewImports.cshtml` file of the `Views` directory. For more information, see [Dependency injection into views](xref:mvc/views/dependency-injection). -Use the injected authorization service to invoke `AuthorizeAsync` in exactly the same way you would check during [resource-based authorization](xref:security/authorization/resourcebased#security-authorization-resource-based-imperative): +Use the injected authorization service to invoke `AuthorizeAsync` in exactly the same way you would check during [resource-based authorization](xref:security/authorization/resource-based#use-imperative-authorization): ```cshtml @if ((await AuthorizationService.AuthorizeAsync(User, "PolicyName")).Succeeded) @@ -27,7 +27,7 @@ Use the injected authorization service to invoke `AuthorizeAsync` in exactly the } ``` -In some cases, the resource will be your view model. Invoke `AuthorizeAsync` in exactly the same way you would check during [resource-based authorization](xref:security/authorization/resourcebased#security-authorization-resource-based-imperative): +In some cases, the resource is your view model. Invoke `AuthorizeAsync` in exactly the same way you would check during [resource-based authorization](xref:security/authorization/resource-based#use-imperative-authorization): ```cshtml @if ((await AuthorizationService.AuthorizeAsync(User, Model, Operations.Edit)).Succeeded) diff --git a/aspnetcore/signalr/authn-and-authz.md b/aspnetcore/signalr/authn-and-authz.md index e219c1f5decd..bb2f450130c9 100644 --- a/aspnetcore/signalr/authn-and-authz.md +++ b/aspnetcore/signalr/authn-and-authz.md @@ -202,7 +202,7 @@ In the preceding example, the `DomainRestrictedRequirement` class is both an `IA ## Additional resources * [Bearer Token Authentication in ASP.NET Core](https://blogs.msdn.microsoft.com/webdev/2016/10/27/bearer-token-authentication-in-asp-net-core/) -* [Resource-based Authorization](xref:security/authorization/resourcebased) +* [Resource-based authorization](xref:security/authorization/resource-based) * [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/signalr/authn-and-authz/sample/) [(how to download)](xref:fundamentals/index#how-to-download-a-sample) :::moniker-end @@ -491,6 +491,6 @@ In the preceding example, the `DomainRestrictedRequirement` class is both an `IA ## Additional resources * [Bearer Token Authentication in ASP.NET Core](https://blogs.msdn.microsoft.com/webdev/2016/10/27/bearer-token-authentication-in-asp-net-core/) -* [Resource-based Authorization](xref:security/authorization/resourcebased) +* [Resource-based authorization](xref:security/authorization/resource-based) :::moniker-end diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index ad01c634932d..816d368a9042 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -574,6 +574,8 @@ items: uid: razor-pages/security/authorization/roles - name: Claim-based authorization uid: razor-pages/security/authorization/claims + - name: Resource-based authorization + uid: razor-pages/security/authorization/resource-based - name: MVC items: - name: Overview @@ -626,6 +628,8 @@ items: uid: mvc/security/authorization/roles - name: Claim-based authorization uid: mvc/security/authorization/claims + - name: Resource-based authorization + uid: mvc/security/authorization/resource-based - name: Blazor items: - name: Overview @@ -2098,7 +2102,7 @@ items: - name: Dependency injection in requirement handlers uid: security/authorization/dependencyinjection - name: Resource-based authorization - uid: security/authorization/resourcebased + uid: security/authorization/resource-based - name: View-based authorization uid: security/authorization/views - name: Limit identity by scheme From ea8b8c1fabbadd4b6974dcdb420a799a84bd6137 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 4 May 2026 13:53:20 -0400 Subject: [PATCH 2/7] Resource-based authz article overhaul --- .../security/authorization/resource-based.md | 4 +- .../security/authorization/resource-based.md | 4 +- .../security/authorization/resource-based.md | 303 +++++++++++------- 3 files changed, 197 insertions(+), 114 deletions(-) diff --git a/aspnetcore/mvc/security/authorization/resource-based.md b/aspnetcore/mvc/security/authorization/resource-based.md index 3e2a8069344d..c1386fe190c3 100644 --- a/aspnetcore/mvc/security/authorization/resource-based.md +++ b/aspnetcore/mvc/security/authorization/resource-based.md @@ -5,14 +5,14 @@ author: wadepickett description: Learn how to implement resource-based authorization in an ASP.NET Core MVC app when an [Authorize] attribute doesn't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 05/01/2026 +ms.date: 05/04/2026 uid: mvc/security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core MVC This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the action that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. diff --git a/aspnetcore/razor-pages/security/authorization/resource-based.md b/aspnetcore/razor-pages/security/authorization/resource-based.md index b096a3370f18..4cedd04f920d 100644 --- a/aspnetcore/razor-pages/security/authorization/resource-based.md +++ b/aspnetcore/razor-pages/security/authorization/resource-based.md @@ -5,14 +5,14 @@ author: wadepickett description: Learn how to implement resource-based authorization in an ASP.NET Core Razor Pages app when an [Authorize] attribute doesn't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 04/29/2026 +ms.date: 05/04/2026 uid: razor-pages/security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core Razor Pages This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the page handler that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. diff --git a/aspnetcore/security/authorization/resource-based.md b/aspnetcore/security/authorization/resource-based.md index f21e73fe5753..062bf8975bdc 100644 --- a/aspnetcore/security/authorization/resource-based.md +++ b/aspnetcore/security/authorization/resource-based.md @@ -5,22 +5,30 @@ author: wadepickett description: Learn how to implement resource-based authorization in an ASP.NET Core app when an Authorize attribute won't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 05/01/2026 +ms.date: 05/04/2026 uid: security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, and data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. -Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the page handler or action that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of a method that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). +This article uses Razor component examples and focuses on Blazor authorization scenarios. For Razor Pages and MVC guidance, see the following resources: - contains a sample app that uses resource-based authorization. +* +* -Examples in this article use *primary constructors*, available in C# 12 (.NET 8) or later. For more information, see [Declare primary constructors for classes and structs (C# documentation tutorial)](/dotnet/csharp/whats-new/tutorials/primary-constructors) and [Primary constructors (C# Guide)](/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors). Sample apps that accompany the article that target versions of .NET earlier than .NET 8 use constructor injection. +Examples in this article use *primary constructors*, available in C# 12 (.NET 8) or later. For more information, see [Declare primary constructors for classes and structs (C# documentation tutorial)](/dotnet/csharp/whats-new/tutorials/primary-constructors) and [Primary constructors (C# Guide)](/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors#primary-constructors). + +## Sample app + +The Blazor Web App sample for this article is the [`BlazorWebAppAuthorization` sample app (`dotnet/AspNetCore.Docs.Samples` GitHub repository)](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/BlazorWebAppAuthorization) ([how to download](xref:index#how-to-download-a-sample)). The sample app uses seeded accounts with preconfigured document objects to demonstrate the examples in this article. For more information, see the sample's README file (`README.md`). + +> [!CAUTION] +> This sample app uses an in-memory database to store user information, which isn't suitable for production scenarios. The sample app is intended for demonstration purposes only and shouldn't be used as a starting point for production apps. ## Use imperative authorization @@ -58,103 +66,75 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . - -> [!NOTE] -> The following example assumes successful authentication with the `User` property set. - -FOR MVC ... +In the following example, which is fully explained in the [Create a resource-based handler](#create-a-resource-based-handler) section, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to view provided document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ): ```csharp -[HttpGet] -public async Task View(Guid documentId) +protected override async Task OnParametersSetAsync() { - Document document = documentRepository.Find(documentId); + var user = (await AuthStateProvider.GetAuthenticationStateAsync()).User; - if (document == null) + if (user.Identity is not null && user.Identity.IsAuthenticated) { - return new NotFoundResult(); - } + var document = DocumentRepository.Find(DocumentId); - if ((await authorizationService - .AuthorizeAsync(User, document, Operations.Read)).Succeeded) - { - return View(document); - } - else - { - return new ChallengeResult(); + ... + + var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, "SameAuthorPolicy"); + + ... } } ``` - +Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). -FOR RP ... +The following `Document` class is used: ```csharp -public async Task OnGetAsync(Guid documentId) +namespace BlazorWebAppAuthorization.Models; + +public class Document { - Document = documentRepository.Find(documentId); + public string? Author { get; set; } - if (Document == null) - { - return new NotFoundResult(); - } + public byte[]? Content { get; set; } - var authorizationResult = await authorizationService - .AuthorizeAsync(User, Document, "EditPolicy"); + public Guid ID { get; set; } - if (authorizationResult.Succeeded) - { - return Page(); - } - else if (User.Identity.IsAuthenticated) - { - return new ForbidResult(); - } - else - { - return new ChallengeResult(); - } + public string? Title { get; set; } } ``` - - -## Create a resource-based handler - -Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). - -The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: - -:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationHandler.cs" id="snippet_HandlerAndRequirement"::: - - + return Task.CompletedTask; + } +} -In the preceding example, imagine that `SameAuthorRequirement` is a special case of a more generic `SpecificAuthorRequirement` class. The `SpecificAuthorRequirement` class (not shown) contains a `Name` property representing the name of the author. The `Name` property could be set to the current user. +public class SameAuthorRequirement : IAuthorizationRequirement { } +``` :::moniker range=">= aspnetcore-6.0" @@ -162,10 +142,11 @@ Register the requirement and handler in `Program.cs`: ```csharp builder.Services.AddAuthorizationBuilder() - .AddPolicy("EditPolicy", policy => + .AddPolicy("SameAuthorPolicy", policy => policy.Requirements.Add(new SameAuthorRequirement())); builder.Services.AddSingleton(); +builder.Services.AddScoped(); ``` :::moniker-end @@ -177,71 +158,173 @@ Register the requirement and handler in `Startup.ConfigureServices`: ```csharp services.AddAuthorization(options => { - options.AddPolicy("EditPolicy", policy => + options.AddPolicy("SameAuthorPolicy", policy => policy.Requirements.Add(new SameAuthorRequirement())); }); -services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); ``` :::moniker-end -### Operational requirements - -To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: +For more information on creating authorization policies, see . -:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_OperationsClass"::: +The following `AccessDocument` component calls an overload to determine whether the current user is allowed to view provided document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). - + + +

Hello, @context.User.Identity?.Name!

+

@message

+
+ +

You're not authorized to access this page.

+
+
-The handler is implemented as follows, using an requirement and a `Document` resource: +@code { + private string? message; -:::code language="csharp" source="~/../AspNetCore.Docs.Samples/security/authorization/resource-based/3.0/ResourceBasedAuthApp2/Services/DocumentAuthorizationCrudHandler.cs" id="snippet_Handler"::: + [Parameter] + public string? DocumentId { get; set; } - + var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, "SameAuthorPolicy"); -The preceding handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. + message = authorizationResult.Succeeded + ? $"You are authorized for document {DocumentId}." + : $"You are NOT authorized for document {DocumentId}."; + } + } +} +``` -## Challenge and forbid with an operational resource handler +In the [sample app](#sample-app), each user of the app is authorized access to the document that they authored. -This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. +## Operational requirements -To call an operational resource handler, specify the operation when invoking in the page handler or action. The following example determines whether the authenticated user is permitted to view the provided document. +To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. The following `Operations` class establishes all four CRUD operation types: -> [!NOTE] -> The following example assumes successful authentication with the `User` property set. +```csharp +using Microsoft.AspNetCore.Authorization.Infrastructure; -FOR MVC ... +namespace BlazorWebAppAuthorization.Services; -```csharp -if ((await authorizationService - .AuthorizeAsync(User, document, Operations.Read)).Succeeded) +public static class Operations { - return View(document); + public static readonly OperationAuthorizationRequirement Create = + new() { Name = nameof(Create) }; + public static readonly OperationAuthorizationRequirement Delete = + new() { Name = nameof(Delete) }; + public static readonly OperationAuthorizationRequirement Read = + new() { Name = nameof(Read) }; + public static readonly OperationAuthorizationRequirement Update = + new() { Name = nameof(Update) }; } -else +``` + +The following authorization handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. + +`Services/DocumentAuthorizationCrudHandler.cs`: + +```csharp +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Infrastructure; +using BlazorWebAppAuthorization.Models; + +namespace BlazorWebAppAuthorization.Services; + +public class DocumentAuthorizationCrudHandler : + AuthorizationHandler { - return new ChallengeResult(); + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, + OperationAuthorizationRequirement requirement, Document resource) + { + if (requirement.Name == Operations.Create.Name && + context.User.IsInRole("Admin")) + { + context.Succeed(requirement); + } + + if (requirement.Name == Operations.Delete.Name && + context.User.IsInRole("SuperUser")) + { + context.Succeed(requirement); + } + + if (requirement.Name == Operations.Read.Name) + { + context.Succeed(requirement); + } + + if (requirement.Name == Operations.Update.Name && + context.User.IsInRole("Admin")) + { + context.Succeed(requirement); + } + + return Task.CompletedTask; + } } ``` -FOR RP ... +Where services are registered in the app: + +```csharp +builder.Services.AddSingleton(); +builder.Services.AddScoped(); +``` + +Call the overload of with the operation to return the authorization result: + +```csharp +var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, Operations.Create); +``` + +```csharp +var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, Operations.Read); +``` ```csharp -var authorizationResult = await authorizationService - .AuthorizeAsync(User, Document, Operations.Read); +var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, Operations.Delete); ``` -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. +```csharp +var authorizationResult = await AuthorizationService + .AuthorizeAsync(user, document, Operations.Update); +``` + +In the [sample app](#sample-app)'s `AccessDocumentCrud` page: + +* Leela (`leela@contoso.com`), as an `Admin` and `SuperUser`, can perform full CRUD operations on resources. +* Harry (`harry@contoso.com`), as only an `Admin`, can create, read, and update resources. +* Sarah (`sarah@contoso.com`), as only a `SuperUser`, can delete and read resources. From 99bbc9720e3d655d7240f8f778531b3337ef9647 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 5 May 2026 10:49:46 -0400 Subject: [PATCH 3/7] Updates --- .../security/authorization/resource-based.md | 31 ++++---- .../security/authorization/resource-based.md | 48 +++++-------- .../security/authorization/resource-based.md | 72 ++++++++++--------- 3 files changed, 70 insertions(+), 81 deletions(-) diff --git a/aspnetcore/mvc/security/authorization/resource-based.md b/aspnetcore/mvc/security/authorization/resource-based.md index c1386fe190c3..a88252b5054b 100644 --- a/aspnetcore/mvc/security/authorization/resource-based.md +++ b/aspnetcore/mvc/security/authorization/resource-based.md @@ -5,16 +5,16 @@ author: wadepickett description: Learn how to implement resource-based authorization in an ASP.NET Core MVC app when an [Authorize] attribute doesn't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 05/04/2026 +ms.date: 05/05/2026 uid: mvc/security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core MVC This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. -Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the action that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of an action that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. [View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). @@ -24,11 +24,11 @@ Examples in this article use *primary constructors*, available in C# 12 (.NET 8) ## Use imperative authorization -Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to actions. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available to classes and actions via [dependency injection](xref:fundamentals/dependency-injection). The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: ```csharp public class DocumentController(IAuthorizationService authorizationService, - IDocumentRepository documentRepository) : Controller + IDocumentRepository documentRepository) : Controller ``` has two method overloads. One of the overloads accepts a resource and policy name: @@ -40,7 +40,7 @@ Task AuthorizeAsync( string policyName); ``` -The other overload accepts a resource and list of requirements to evaluate: +The other overload accepts a resource and collection of requirements () to evaluate: ```csharp Task AuthorizeAsync( @@ -49,24 +49,21 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit to the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). > [!NOTE] > The following example assumes successful authentication with the `User` property set. ```csharp [HttpGet] -public async Task View(Guid documentId) +public async Task Edit(Guid documentId) { - Document document = documentRepository.Find(documentId); + Document document = _documentRepository.Find(documentId); - if (document == null) - { - return new NotFoundResult(); - } + ... - if ((await authorizationService - .AuthorizeAsync(User, document, Operations.Read)).Succeeded) + if ((await _authorizationService + .AuthorizeAsync(User, document, "EditPolicy")).Succeeded) { return View(document); } @@ -89,7 +86,7 @@ https://github.com/dotnet/AspNetCore.Docs.Samples/blob/main/security/authorizati ## Create a resource-based handler -Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). +Creating a resource-based authorization handler is similar to [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: @@ -135,6 +132,8 @@ services.AddSingleton(); :::moniker-end +For more information on creating authorization policies, see . + ### Operational requirements To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: diff --git a/aspnetcore/razor-pages/security/authorization/resource-based.md b/aspnetcore/razor-pages/security/authorization/resource-based.md index 4cedd04f920d..7b9f191d8788 100644 --- a/aspnetcore/razor-pages/security/authorization/resource-based.md +++ b/aspnetcore/razor-pages/security/authorization/resource-based.md @@ -5,16 +5,16 @@ author: wadepickett description: Learn how to implement resource-based authorization in an ASP.NET Core Razor Pages app when an [Authorize] attribute doesn't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 05/04/2026 +ms.date: 05/05/2026 uid: razor-pages/security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core Razor Pages This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. -Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of the page handler that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of a page handler that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. [View or download sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/resource-based) ([how to download](xref:fundamentals/index#how-to-download-a-sample)). @@ -24,11 +24,11 @@ Examples in this article use *primary constructors*, available in C# 12 (.NET 8) ## Use imperative authorization -Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available to classes and page handlers via [dependency injection](xref:fundamentals/dependency-injection). The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: ```csharp public class DocumentModel(IAuthorizationService authorizationService, - IDocumentRepository documentRepository) : PageModel + IDocumentRepository documentRepository) : PageModel ``` has two method overloads. One of the overloads accepts a resource and policy name: @@ -40,7 +40,7 @@ Task AuthorizeAsync( string policyName); ``` -The other overload accepts a resource and list of requirements to evaluate: +The other overload accepts a resource and collection of requirements () to evaluate: ```csharp Task AuthorizeAsync( @@ -49,7 +49,7 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the provided document. A custom "`EditPolicy`" authorization policy is factored into the decision. For more information on creating authorization policies, see . +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit to the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). > [!NOTE] > The following example assumes successful authentication with the `User` property set. @@ -57,28 +57,14 @@ In the following example, the secured resource is loaded into a custom `Document ```csharp public async Task OnGetAsync(Guid documentId) { - Document = documentRepository.Find(documentId); - - if (Document == null) - { - return new NotFoundResult(); - } - - var authorizationResult = await authorizationService - .AuthorizeAsync(User, Document, "EditPolicy"); - - if (authorizationResult.Succeeded) - { - return Page(); - } - else if (User.Identity.IsAuthenticated) - { - return new ForbidResult(); - } - else - { - return new ChallengeResult(); - } + Document = _documentRepository.Find(documentId); + + ... + + var authorizationResult = await _authorizationService + .AuthorizeAsync(User, Document, "EditPolicy"); + + ... } ``` @@ -94,7 +80,7 @@ https://github.com/dotnet/AspNetCore.Docs.Samples/blob/main/security/authorizati ## Create a resource-based handler -Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). +Creating a resource-based authorization handler is similar to [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). The handler class specifies the requirement and resource type. The following example demonstrates a handler utilizing a `SameAuthorRequirement` requirement and a `Document` resource: @@ -140,6 +126,8 @@ services.AddSingleton(); :::moniker-end +For more information on creating authorization policies, see . + ### Operational requirements To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. This class enables you to write a single handler instead of an individual class for each operation type. To use it, provide some operation names: diff --git a/aspnetcore/security/authorization/resource-based.md b/aspnetcore/security/authorization/resource-based.md index 062bf8975bdc..238f6db8d28e 100644 --- a/aspnetcore/security/authorization/resource-based.md +++ b/aspnetcore/security/authorization/resource-based.md @@ -2,19 +2,19 @@ title: Resource-based authorization in ASP.NET Core ai-usage: ai-assisted author: wadepickett -description: Learn how to implement resource-based authorization in an ASP.NET Core app when an Authorize attribute won't suffice. +description: Learn how to implement resource-based authorization in an ASP.NET Core app when an [Authorize] attribute doesn't suffice. ms.author: wpickett ms.custom: mvc -ms.date: 05/04/2026 +ms.date: 05/05/2026 uid: security/authorization/resource-based --- # Resource-based authorization in ASP.NET Core This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a resource ID, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, cloud storage objects, in-memory objects, or data stored in databases. +In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. -Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of a method that loads a resource. For these reasons, declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app invokes a custom authorization method—an approach known as *imperative authorization*. +Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of any method that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. This article uses Razor component examples and focuses on Blazor authorization scenarios. For Razor Pages and MVC guidance, see the following resources: @@ -32,20 +32,11 @@ The Blazor Web App sample for this article is the [`BlazorWebAppAuthorization` s ## Use imperative authorization -Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available via [dependency injection](xref:fundamentals/dependency-injection) to page handlers or actions. The following example also injects a document repository, which the developer creates and registers in the service container to manage document operations: +Authorization is implemented as an , which is registered in the service collection at app startup *by the ASP.NET Core framework*. The service is made available to Razor components and other classes via [dependency injection](xref:fundamentals/dependency-injection): -FOR MVC ... - -```csharp -public class DocumentController(IAuthorizationService authorizationService, - IDocumentRepository documentRepository) : Controller -``` - -FOR RP ... - -```csharp -public class DocumentModel(IAuthorizationService authorizationService, - IDocumentRepository documentRepository) : PageModel +```razor +@using Microsoft.AspNetCore.Authorization +@inject IAuthorizationService AuthorizationService ``` has two method overloads. One of the overloads accepts a resource and policy name: @@ -57,7 +48,7 @@ Task AuthorizeAsync( string policyName); ``` -The other overload accepts a resource and list of requirements to evaluate: +The other overload accepts a resource and collection of requirements () to evaluate: ```csharp Task AuthorizeAsync( @@ -66,7 +57,7 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, which is fully explained in the [Create a resource-based handler](#create-a-resource-based-handler) section, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to view provided document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ): +In the following example, which is fully explained in the [Create a resource-based handler](#create-a-resource-based-handler) section, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed access to the document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ): ```csharp protected override async Task OnParametersSetAsync() @@ -89,9 +80,9 @@ protected override async Task OnParametersSetAsync() ## Create a resource-based handler -Creating a resource-based authorization handler isn't much different than [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). +Creating a resource-based authorization handler is similar to [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). -The following `Document` class is used: +The following demonstration `Document` class is used: ```csharp namespace BlazorWebAppAuthorization.Models; @@ -121,8 +112,10 @@ namespace BlazorWebAppAuthorization.Services; public class DocumentAuthorizationHandler : AuthorizationHandler { - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, - SameAuthorRequirement requirement, Document resource) + protected override Task HandleRequirementAsync( + AuthorizationHandlerContext context, + SameAuthorRequirement requirement, + Document resource) { if (context.User.Identity?.Name == resource.Author) { @@ -146,7 +139,6 @@ builder.Services.AddAuthorizationBuilder() policy.Requirements.Add(new SameAuthorRequirement())); builder.Services.AddSingleton(); -builder.Services.AddScoped(); ``` :::moniker-end @@ -163,14 +155,13 @@ services.AddAuthorization(options => }); builder.Services.AddSingleton(); -builder.Services.AddScoped(); ``` :::moniker-end For more information on creating authorization policies, see . -The following `AccessDocument` component calls an overload to determine whether the current user is allowed to view provided document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). +The following `AccessDocument` component calls an overload to determine whether the current user is allowed to view a document based on the "`SameAuthorPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). `Pages/AccessDocument.razor`: @@ -225,17 +216,15 @@ The following `AccessDocument` component calls an helper class. This class enables you to write a single handler instead of an individual class for each operation type. The following `Operations` class establishes all four CRUD operation types: +To make decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the helper class. The helper class enables you to write a single handler instead of an individual class for each operation type. The following `Operations` class establishes all four CRUD operation types: ```csharp using Microsoft.AspNetCore.Authorization.Infrastructure; -namespace BlazorWebAppAuthorization.Services; - public static class Operations { public static readonly OperationAuthorizationRequirement Create = @@ -249,7 +238,11 @@ public static class Operations } ``` -The following authorization handler validates the operation using the resource, the user's identity, and the requirement's `Name` property. +The following `DocumentAuthorizationCrudHandler` authorization handler validates the operation using the resource, the user's identity (role) in some cases, and the requirement's `Name` property: + +* All users can read documents. +* Only users in the `Admin` role can create and update documents. +* Only users in the `SuperUser` role can delete documents. `Services/DocumentAuthorizationCrudHandler.cs`: @@ -263,8 +256,10 @@ namespace BlazorWebAppAuthorization.Services; public class DocumentAuthorizationCrudHandler : AuthorizationHandler { - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, - OperationAuthorizationRequirement requirement, Document resource) + protected override Task HandleRequirementAsync( + AuthorizationHandlerContext context, + OperationAuthorizationRequirement requirement, + Document resource) { if (requirement.Name == Operations.Create.Name && context.User.IsInRole("Admin")) @@ -298,26 +293,33 @@ Where services are registered in the app: ```csharp builder.Services.AddSingleton(); -builder.Services.AddScoped(); ``` -Call the overload of with the operation to return the authorization result: +Call the overload of with the operation to return the authorization result. + +For authorization to *create* a document: ```csharp var authorizationResult = await AuthorizationService .AuthorizeAsync(user, document, Operations.Create); ``` +For authorization to *read* a document: + ```csharp var authorizationResult = await AuthorizationService .AuthorizeAsync(user, document, Operations.Read); ``` +For authorization to *delete* a document: + ```csharp var authorizationResult = await AuthorizationService .AuthorizeAsync(user, document, Operations.Delete); ``` +For authorization to *update* a document: + ```csharp var authorizationResult = await AuthorizationService .AuthorizeAsync(user, document, Operations.Update); From c9cb0c5a5a6114e13704c9ccb5169b643d64c49c Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 5 May 2026 11:20:30 -0400 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../mvc/security/authorization/resource-based.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/aspnetcore/mvc/security/authorization/resource-based.md b/aspnetcore/mvc/security/authorization/resource-based.md index a88252b5054b..37bdbf86fe4c 100644 --- a/aspnetcore/mvc/security/authorization/resource-based.md +++ b/aspnetcore/mvc/security/authorization/resource-based.md @@ -12,7 +12,7 @@ uid: mvc/security/authorization/resource-based This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining to the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of an action that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. @@ -49,7 +49,7 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit to the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). > [!NOTE] > The following example assumes successful authentication with the `User` property set. @@ -69,7 +69,7 @@ public async Task Edit(Guid documentId) } else { - return new ChallengeResult(); + return new ForbidResult(); } } ``` @@ -177,6 +177,10 @@ if ((await authorizationService { return View(document); } +else if (User.Identity?.IsAuthenticated ?? false) +{ + return new ForbidResult(); +} else { return new ChallengeResult(); From 0766f367e249e1f3089391c7a636f21b10b117d6 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 5 May 2026 11:48:49 -0400 Subject: [PATCH 5/7] Updates --- .../security/authorization/resource-based.md | 27 +++------------- .../security/authorization/resource-based.md | 31 ++++++++++--------- 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/aspnetcore/mvc/security/authorization/resource-based.md b/aspnetcore/mvc/security/authorization/resource-based.md index 37bdbf86fe4c..0f3d5c758532 100644 --- a/aspnetcore/mvc/security/authorization/resource-based.md +++ b/aspnetcore/mvc/security/authorization/resource-based.md @@ -62,28 +62,13 @@ public async Task Edit(Guid documentId) ... - if ((await _authorizationService - .AuthorizeAsync(User, document, "EditPolicy")).Succeeded) - { - return View(document); - } - else - { - return new ForbidResult(); - } + var authorizationResult = await _authorizationService + .AuthorizeAsync(User, document, "EditPolicy"); + + ... } ``` - - ## Create a resource-based handler Creating a resource-based authorization handler is similar to [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). @@ -166,7 +151,7 @@ The preceding handler validates the operation using the resource, the user's ide This section shows how the challenge and forbid action results are processed and how challenge and forbid differ. -To call an operational resource handler, specify the operation when invoking in the action. The following example determines whether the authenticated user is permitted to view the provided document. +When authorization fails but the user is authenticated, the app can return a , which informs authentication middleware that authorization failed. Return a for unauthenticated users. For interactive browser clients, it may be appropriate to redirect the user to a login page. > [!NOTE] > The following example assumes successful authentication with the `User` property set. @@ -186,5 +171,3 @@ else return new ChallengeResult(); } ``` - -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. diff --git a/aspnetcore/razor-pages/security/authorization/resource-based.md b/aspnetcore/razor-pages/security/authorization/resource-based.md index 7b9f191d8788..2c6d9aa4622e 100644 --- a/aspnetcore/razor-pages/security/authorization/resource-based.md +++ b/aspnetcore/razor-pages/security/authorization/resource-based.md @@ -68,16 +68,6 @@ public async Task OnGetAsync(Guid documentId) } ``` - - ## Create a resource-based handler Creating a resource-based authorization handler is similar to [creating a plain requirements handler](xref:security/authorization/policies#security-authorization-policies-based-authorization-handler). Create a custom requirement class and implement a requirement handler class. For more information on creating a requirement class, see the [Policy-based authorization: Requirements](xref:security/authorization/policies#requirements). @@ -160,14 +150,25 @@ The preceding handler validates the operation using the resource, the user's ide This section shows how the challenge and forbid results are processed and how challenge and forbid differ. -To call an operational resource handler, specify the operation when invoking in the page handler. The following example determines whether the authenticated user is permitted to view the provided document. +When authorization fails but the user is authenticated, the app can return a , which informs authentication middleware that authorization failed. Return a for unauthenticated users. For interactive browser clients, it may be appropriate to redirect the user to a login page. > [!NOTE] > The following example assumes successful authentication with the `User` property set. ```csharp -var authorizationResult = await authorizationService - .AuthorizeAsync(User, Document, Operations.Read); -``` +var authorizationResult = await _authorizationService + .AuthorizeAsync(User, Document, Operations.Read); -If authorization succeeds, the page for viewing the document is returned. If authorization fails but the user is authenticated, returning informs authentication middleware that authorization failed. A is returned when authentication is required. For interactive browser clients, it may be appropriate to redirect the user to a login page. +if (authorizationResult.Succeeded) +{ + return Page(); +} +else if (User.Identity?.IsAuthenticated ?? false) +{ + return new ForbidResult(); +} +else +{ + return new ChallengeResult(); +} +``` From 7d766ad5f1efb8f278345c0306590c7a9c5106e5 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 5 May 2026 11:52:31 -0400 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../razor-pages/security/authorization/resource-based.md | 4 ++-- aspnetcore/security/authentication/index.md | 6 +++--- aspnetcore/security/authorization/resource-based.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aspnetcore/razor-pages/security/authorization/resource-based.md b/aspnetcore/razor-pages/security/authorization/resource-based.md index 2c6d9aa4622e..ca870038ad6a 100644 --- a/aspnetcore/razor-pages/security/authorization/resource-based.md +++ b/aspnetcore/razor-pages/security/authorization/resource-based.md @@ -12,7 +12,7 @@ uid: razor-pages/security/authorization/resource-based This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining to the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of a page handler that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. @@ -49,7 +49,7 @@ Task AuthorizeAsync( IEnumerable requirements); ``` -In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit to the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). +In the following example, the secured resource is loaded into a custom `Document` object. An overload is invoked to determine whether the current user is allowed to edit the document via a custom "`EditPolicy`" authorization policy. If [`authorizationResult.Succeeded`](xref:Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded%2A) is `true`, the user is authorized for the document because they authored the document (`Document.Author` matches the user's ). > [!NOTE] > The following example assumes successful authentication with the `User` property set. diff --git a/aspnetcore/security/authentication/index.md b/aspnetcore/security/authentication/index.md index 3c3c6be5a9f5..e5bea0b0cb5a 100644 --- a/aspnetcore/security/authentication/index.md +++ b/aspnetcore/security/authentication/index.md @@ -133,7 +133,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). +* [Resource-based authorization](xref:security/authorization/resource-based). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant @@ -281,7 +281,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). +* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant @@ -424,7 +424,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). +* [Resource-based authorization](xref:security/authorization/resource-based). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant diff --git a/aspnetcore/security/authorization/resource-based.md b/aspnetcore/security/authorization/resource-based.md index 238f6db8d28e..6328c587b94e 100644 --- a/aspnetcore/security/authorization/resource-based.md +++ b/aspnetcore/security/authorization/resource-based.md @@ -12,7 +12,7 @@ uid: security/authorization/resource-based This article describes how to authorize users for access to app resources. -In an app, a *resource* is typically represented by a C# class that includes data stored in collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. +In an app, a *resource* is typically represented by a C# class that includes data stored in a collection, such as a [`byte[]` array](xref:System.Byte). The class usually contains additional metadata pertaining to the resource, such as a unique resource identifier, dates, authors, source information, and a friendly name for display in a UI. The collection that holds resource data is usually loaded from physical file content, a cloud storage object, an in-memory object, or data from a database. Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of any method that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. @@ -154,7 +154,7 @@ services.AddAuthorization(options => policy.Requirements.Add(new SameAuthorRequirement())); }); -builder.Services.AddSingleton(); +services.AddSingleton(); ``` :::moniker-end From 3f7f2a091c56d41732d02e4c826a1133aa277ea4 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 5 May 2026 12:05:34 -0400 Subject: [PATCH 7/7] Updates --- aspnetcore/security/authentication/index.md | 2 +- aspnetcore/security/authorization/resource-based.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/security/authentication/index.md b/aspnetcore/security/authentication/index.md index e5bea0b0cb5a..b9aa4a89f741 100644 --- a/aspnetcore/security/authentication/index.md +++ b/aspnetcore/security/authentication/index.md @@ -281,7 +281,7 @@ A forbid action can let the user know: See the following links for differences between challenge and forbid: -* [Challenge and forbid with an operational resource handler](xref:security/authorization/resource-based). +* [Challenge and forbid with an operational resource handler](xref:razor-pages/security/authorization/resource-based#challenge-and-forbid-with-an-operational-resource-handler). * [Differences between challenge and forbid](xref:security/authorization/secure-data#challenge). ## Authentication providers per tenant diff --git a/aspnetcore/security/authorization/resource-based.md b/aspnetcore/security/authorization/resource-based.md index 6328c587b94e..7fa14aa59e97 100644 --- a/aspnetcore/security/authorization/resource-based.md +++ b/aspnetcore/security/authorization/resource-based.md @@ -16,7 +16,7 @@ In an app, a *resource* is typically represented by a C# class that includes dat Resource-based authorization requires special attention in ASP.NET Core apps. Attribute evaluation occurs before data binding and before execution of any method that loads a resource. Declarative authorization with an `[Authorize]` attribute doesn't suffice for resource-based authorization. Instead, the app must invoke a custom authorization method—an approach known as *imperative authorization*. -This article uses Razor component examples and focuses on Blazor authorization scenarios. For Razor Pages and MVC guidance, see the following resources: +This article uses Razor component examples and focuses on [Blazor](xref:blazor/index) authorization scenarios for ASP.NET Core 3.1 or later. For Razor Pages and MVC guidance, which apply to all releases of ASP.NET Core, see the following resources: * *