Swacblooms🦋

Making the Moves
Menu
  • Home
  • Motivation
  • Education
  • Programming
  • About
  • Contact
  • Privacy Policy
Home
Programming
Exploring Policy-Based Authorization in Minimal API with .NET 8
Programming

Exploring Policy-Based Authorization in Minimal API with .NET 8

Samson Amaugo August 26, 2023

Hey y’all 👋, So let us do some learning on Policies in Asp.Net. The release of .NET 8 Preview 7 comes with a new bearer token authentication handler. This eases setting up authentication for minimal APIs using JWT.

In this post, I will be using the new bearer token authentication handler to handle authentication for the minimal API endpoint.

Policies in ASP.NET enable you to easily configure authorization by setting up rules and checks in the authorization pipeline. These checks determine if access will be granted to the request to an endpoint.

There are two important things to note when defining policies:

  • Requirements
  • Requirements Handler

Requirement

Requirements are used to define the conditions that a user must meet to access a resource or perform an operation.

Requirements are usually created by implementing the IAuthorizationRequirement and setting properties that hold the required values to be checked by the handlers.

// A simple class to define a location requirement
public class LocationRequirement : IAuthorizationRequirement
{
    public string Location { get; set; }
    public LocationRequirement(string location)
    {
        Location = location;
    }
}

In the code above, the LocationRequirement class requires that the location property be set during class instantiation. Mostly done during Policy definition.

Requirements Handler

The requirements handler is used to check the user’s claims or details against the value set in the Requirement class. If the check is valid, access is granted to the route; otherwise, access is restricted.

Access is granted when the Succeed method on the AuthorizationHandlerContext is called with the requirement passed into the Succeed method.

Authorization Handlers can be defined in different ways based on the interface or generic type that they inherit. The basic means of defining a handler is by implementing the IAuthorizationHandler.

public class LocationHandler : IAuthorizationHandler{
    public Task HandleAsync(AuthorizationHandlerContext context){
        var locationRequirement = context.Requirements.OfType<LocationRequirement>().FirstOrDefault();
        if(LocationRequirement != null){
            if(context.User.FindFirst(ClaimTypes.Locality).Value == locationRequirement.Location){
                context.Succeed(LocationRequirement);
            }
        }
        return Task.CompletedTask;
    }
}

The problem with handlers implementing the IAuthorizatonHandler is that they get processed regardless of whether the policy for a specific endpoint defines it. But they can come in handy when you want to add global handlers to the authorization pipeline.

Another way the handler can be defined is by implementing the AuthorizationHandler<TRequirement>. This makes the handler to be processed only when the endpoint/route that is requested from the client has a policy that adds the <TRequirement>.

The example below basically does the same thing as the one above but doesn’t run globally and is also shorter to write.

public class LocationHandler2 : AuthorizationHandler<LocationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LocationRequirement requirement)
    {
        if (context.User.FindFirstValue(ClaimTypes.Locality) == requirement.Location)
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

And finally, one important step to not forget is registering your handler in the Service Collection.

service.AddSingleton<IAuthorizationHandler, LocationHandler>();

Securing your API with Policy-Base Authorization

This application configures an endpoint with a policy that only allows users with email addresses containing the word "sam" to access the resource. You’ll need to have at least .NET 8 Preview 7 to run this code.

Steps Taken:

  • Run "dotnet new web -o miniAPIAuthorizationPolicy" to create a minimal API project in a folder called "miniAPIAuthorizationPolicy"
  • Install Some Packages by executing the command below:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
  • To enable preview features like the primary constructors in C# 12 Add this flag to your .csproj file inside the PropertyGroup tag.
<LangVersion>preview</LangVersion>
  • Replace the code in the Program.cs file with the one below.
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);
// Add Bearer Token Authentication Handler
builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);

// Add Authorization Policy and add a requirement to the policy
// Decided to add the policy name to the requirement class
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(EmailContainsRequirement.Policy, x =>
    {
        x.AddRequirements(new EmailContainsRequirement("sam"));
    });
});

// Add the requirement handler to the service collection
builder.Services.AddSingleton<IAuthorizationHandler, EmailContainsHandler>();
builder.Services.AddDbContext<ApplicationDbContext>(x => x.UseSqlite("DataSource=datastore.db"));
builder.Services.AddIdentityCore<IdentityUser>()
   .AddEntityFrameworkStores<ApplicationDbContext>()
   .AddApiEndpoints();
var app = builder.Build();

// registers the Identity API endpoints
app.MapIdentityApi<IdentityUser>();

// Add a route that requires the EmailContainsRequirement.Policy Policy
// The policy is added to the route using the RequireAuthorization method
app.MapGet("/contains-sam", (ClaimsPrincipal principal) =>
{
    var email = principal.FindFirstValue(ClaimTypes.Email);
    return $"Your Email is : {email}";

}).RequireAuthorization(EmailContainsRequirement.Policy);

app.Run();

public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }
}

// The EmailContainsRequirement class sets the substring to search for in the email
// of the authenticated user.
public class EmailContainsRequirement(string substring) : IAuthorizationRequirement
{
    public string SubString = substring;
    public const string Policy = "EmailContainsSubStringPolicy";
}

// The EmailContainsHandler class checks if the authenticated user's email contains
// the substring specified in the requirement.
public class EmailContainsHandler : AuthorizationHandler<EmailContainsRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EmailContainsRequirement requirement)
    {
        if (context.User.FindFirstValue(ClaimTypes.Email).Contains(requirement.SubString))
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}
  • The code above makes use of SQLite and won’t work until you make migrations of the IdentityUser types.
    To do this, execute the command below:
dotnet ef migrations add <any-migraton-name-of-your-choice>
  • Next, create the tables in your SQL database based on the created migration by executing the command below:
dotnet ef database update
  • Start the application by running "dotnet run".
  • On my end, my API starts at http://localhost:5111 . Ensure to change the port to yours.
  • From your terminal execute the command below to register a user having “testuser@mail.com” as an email address.
curl --location -v 'http://localhost:5111/register' \
--header 'Content-Type: application/json' \
--data-raw '{
   "username": "testuser",
  "password": "TestPassword1$",
  "email": "testuser@mail.com"
}'
  • To log in and obtain an access token, execute the code below
curl --location -v 'http://localhost:5111/login' \
--header 'Content-Type: application/json' \
--data '{
    "username": "testuser",
    "password": "TestPassword1$"
}'

Copy the access token from the response and use it to query the "contains-sam" end-point

  • Execute the code below to query the "contains-sam" endpoint.
curl --location -v 'http://localhost:5111/contains-sam' \
--header 'Authorization: Bearer <Your access token>'

You should receive a 403 Forbidden status in the response header. This is because the user’s email (“testuser@mail.com”) doesn’t contain the substring “sam”.

But if a user registers with any email address that contains “sam” eg (tesame@mail.com, casamik@mail.com). The user should be able to access that route and receive a response.

For instance, if a user with the email: samike@mail.com registers and logs in, the token retrieved during login should permit the user to access the “/contains-sam” endpoint.

Some things to Note when using Policy-Based Authorization

  • If none of the handlers for policy requirement calls the "Succeed" method of the AuthorizationHandlerContext, the user would be restricted from accessing the endpoint.
  • If only one of the handlers for a policy requirement calls "Succeed" method of the AuthorizationHandlerContext, the user would be allowed access to the endpoint as long as no handler calls the "Fail" method of the AuthorizationHandlerContext.

In ASP.NET, Policy-Based Authorization facilitates the modular organization of your authorization logic throughout your application.

The code used here can be found on my GitHub. To learn more about Policy-based Authorization in ASP.NET check here.

Thanks for reading through and don’t fail to share it if you find it useful 😄.

Prev Article
Next Article

Related Articles

Grain Persistence in Microsoft Orleans
Hello 👋, so in today’s article I will be writing …

Creating a Custom File Storage Provider for Microsoft Orleans

adjusting your processor speed in ubuntu
Hello guys, in this post I would be putting down …

Adjusting your processor speed in Ubuntu

About The Author

Samson Amaugo

I am Samson Amaugo. I am a full-stack developer and I specialize in DotNet and the MERN stack.

16 Comments

  1. Reminder: Process №OR49. RECEIVE => https://telegra.ph/Go-to-your-personal-cabinet-08-25?hs=3503180e199611fbf3b2426c3975a850&

    3yslr5

    October 1, 2024
  2. You have received a notification # 145. Read > https://telegra.ph/Go-to-your-personal-cabinet-08-25?hs=3503180e199611fbf3b2426c3975a850&

    qb5sg7

    October 15, 2024
  3. Reminder; Process NoUX52. VERIFY >> https://telegra.ph/Go-to-your-personal-cabinet-08-25?hs=3503180e199611fbf3b2426c3975a850&

    0lmvyc

    November 7, 2024
  4. Email; Process 1,8200 BTC. Withdraw > https://telegra.ph/Go-to-your-personal-cabinet-08-25?hs=3503180e199611fbf3b2426c3975a850&

    aije2y

    December 2, 2024
  5. Sending a transfer from Binance. Verify => https://telegra.ph/Go-to-your-personal-cabinet-08-25?hs=3503180e199611fbf3b2426c3975a850&

    kah456

    December 10, 2024
  6. Notification: Operation #HS56. CONFIRM >> https://telegra.ph/Message--2868-12-25?hs=3503180e199611fbf3b2426c3975a850&

    dojtgs

    January 7, 2025
  7. Email- TRANSACTION 0.7575237 BTC. Receive => https://telegra.ph/Get-BTC-right-now-01-22?hs=3503180e199611fbf3b2426c3975a850&

    692t79

    February 7, 2025
  8. + 0.75308017 BTC.GET - https://telegra.ph/Get-BTC-right-now-02-10?hs=3503180e199611fbf3b2426c3975a850&

    mgyxqx

    February 13, 2025
  9. Sending a transfer from our company. Take >> https://graph.org/GET-BITCOIN-TRANSFER-02-23-2?hs=3503180e199611fbf3b2426c3975a850&

    woakiq

    February 28, 2025
  10. Email: Transfer NoYG96. CONFIRM >>> https://graph.org/GET-BITCOIN-TRANSFER-02-23-2?hs=3503180e199611fbf3b2426c3975a850&

    wlf8gd

    March 3, 2025
  11. + 0.75886716 BTC.GET - https://telegra.ph/Binance-Support-02-18?hs=3503180e199611fbf3b2426c3975a850&

    3bg4id

    March 9, 2025
  12. We send a transfer from user. Gо tо withdrаwаl => https://graph.org/GET-BITCOIN-TRANSFER-02-23-2?hs=3503180e199611fbf3b2426c3975a850&

    llxz8u

    March 16, 2025
  13. + 1.907695 BTC.NEXT - https://graph.org/Message--120154-03-25?hs=3503180e199611fbf3b2426c3975a850&

    in10yz

    March 30, 2025
  14. + 1.912479 BTC.NEXT - https://graph.org/Message--04804-03-25?hs=3503180e199611fbf3b2426c3975a850&

    0i089e

    April 8, 2025
  15. + 1.254828 BTC.GET - https://graph.org/Message--17856-03-25?hs=3503180e199611fbf3b2426c3975a850&

    utbadq

    April 16, 2025
  16. + 1.466408 BTC.GET - https://graph.org/Ticket--58146-05-02?hs=3503180e199611fbf3b2426c3975a850&

    putr2l

    May 4, 2025

Leave a Reply

Cancel reply

Search Site

Recent Posts

  • Running Entity Framework Migrations in an Aspire-Bootstrapped Orleans Project
  • Using XDebug in Laravel Sail (VSCODE)
  • Custom Redis Streams Provider for Orleans
  • Creating a Custom File Storage Provider for Microsoft Orleans
  • Incremental Generators In C#

Categories

  • EDUCATION
  • Motivation
  • Programming
  • Uncategorized

Get more stuff

Subscribe to our mailing list and get interesting stuff and updates to your email inbox.

Thank you for subscribing.

Something went wrong.

we respect your privacy and take protecting it seriously

RSS Swacblooms

  • Running Entity Framework Migrations in an Aspire-Bootstrapped Orleans Project
  • Using XDebug in Laravel Sail (VSCODE)
  • Custom Redis Streams Provider for Orleans
  • Creating a Custom File Storage Provider for Microsoft Orleans
  • Incremental Generators In C#
  • Hot Chocolate Data Loader: A Quick Guide
  • Exploring Policy-Based Authorization in Minimal API with .NET 8
  • Using Cloud Firestore in Blazor WebAssembly
  • Serving A VueJs App from DotNet
  • Dynamic Subscriptions in Hot Chocolate 🔥🍫

Swacblooms🦋

Making the Moves
Copyright © 2025 Swacblooms🦋
Swacblooms - Making the Moves