Securing Endpoints with JWT Bearer Authentication in .NET 6 Minimal API using FastEndpoints

Abdullah Ilgaz
3 min readDec 4, 2022

--

In this article, I will show you how to secure endpoints in .NET 6 Minimal API using FastEndpoints. I will also show you how to use JWT Bearer Authentication to secure endpoints.

Getting started

First of all, we need to create a new .NET 6 Minimal API project and add FastEndpoints NuGet package to it. To do that, we need to run the following commands:

dotnet new web -o ProjectName
cd ProjectName
dotnet add package FastEndpoints

Registering FastEndpoints

Add FastEndpoints to Program.cs file. It will look like this:

Program.cs

global using FastEndpoints;

var builder = WebApplication.CreateBuilder();
builder.Services.AddFastEndpoints();

var app = builder.Build();
app.UseAuthorization();
app.UseFastEndpoints();
app.Run();

Now we need to add Security package to our project. To do that, we need to run the following command:

dotnet add package FastEndpoints.Security

Securing endpoints

To secure endpoints in .NET 6 Minimal API using FastEndpoints, we need to add global reference for FastEndpoints.Security. Also we need to add AddAuthenticationJwtBearer extension method to our IServiceCollection and adding UseAuthentication and UseAuthorization middleware to our Program.cs. It will look like this:

Program.cs

global using FastEndpoints;
global using FastEndpoints.Security; // add this line

var builder = WebApplication.CreateBuilder();
builder.Services.AddFastEndpoints();
builder.Services.AddAuthenticationJWTBearer("TokenSigningKey"); // add this line

var app = builder.Build();
app.UseAuthentication(); // add this line
app.UseAuthorization(); // add this line
app.UseFastEndpoints();
app.Run();

Generating JWT Bearer Authentication token

We can generate a JWT token for sending to the client with an endpoint that signs in users like so:

public class UserLoginEndpoint : Endpoint<LoginRequest>
{
public override void Configure()
{
Post("/api/login");
AllowAnonymous();
}

public override async Task HandleAsync(LoginRequest req, CancellationToken ct)
{
if (await authService.CredentialsAreValid(req.Username, req.Password, ct))
{
var jwtToken = JWTBearer.CreateToken(
signingKey: "TokenSigningKey",
expireAt: DateTime.UtcNow.AddDays(1),
claims: new[] { ("Username", req.Username), ("UserID", "001") },
roles: new[] { "Admin", "Management" },
permissions: new[] { "ManageInventory", "ManageUsers" });
await SendAsync(new
{
Username = req.Username,
Token = jwtToken
});
}
else
{
ThrowError("The supplied credentials are invalid!");
}
}
}

Endpoint authorization

Once an authentication provider is registered such as JWT Bearer as shown above, you can restrict access to users based on the following:

  • Policies
  • Claims
  • Roles
  • Permissions

Policies

We can register policies in our Program.cs file like so:

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireRole("Admin"));
options.AddPolicy("Management", policy => policy.RequireRole("Management"));
options.AddPolicy("ManageInventory", policy => policy.RequireClaim("ManageInventory"));
options.AddPolicy("ManageUsers", policy => policy.RequireClaim("ManageUsers"));
});

We can then use these policies in our endpoints like so:

public class UpdateUserEndpoint : Endpoint<UpdateUserRequest>
{
public override void Configure()
{
Put("/api/users/update");
Policies("ManageUsers");
}
}

Declarative Security Policies

Instead of registering each security policy at startup you can selectively specify security requirements for each endpoint in the endpoint configuration. This is useful for endpoints that require different security requirements.

public class RestrictedEndpoint : Endpoint<RestrictedRequest>
{
public override void Configure()
{
Post("/api/restricted");
Claims("AdminID", "EmployeeID");
Roles("Admin", "Manager");
Permissions("UpdateUsersPermission", "DeleteUsersPermission");
}
}

Claims() method

With this method you are specifying that if a user principal has ANY of the specified claims, access should be allowed. if the requirement is to allow access only if ALL specified claims are present, you can use the ClaimsAll() method.

Permissions() method

Just like above, you can specify that ANY of the specified permissions should allow access. Or require ALL of the specified permissions by using the PermissionsAll() method.

Roles() method

Similarly, you are specifying that ANY of the given roles should allow access to a user principal who has it.

AllowAnonymous() method

Use this method if you’d like to allow unauthenticated users to access a particular endpoint.

HTTP Verbs

It is also possible to specify which http verbs you’d like to allow anonymous access to if you’re endpoint is listening on multiple verbs & routes like so:

public class RestrictedEndpoint : Endpoint<RestrictedRequest>
{
public override void Configure()
{
Verbs(Http.POST, Http.PUT, Http.PATCH);
Routes("/api/restricted");
AllowAnonymous(Http.POST);
}
}

The above endpoint is listening for all 3 http methods on the same route but only POST method is allowed to be accessed anonymously. It is useful for example when you’d like to use the same handler logic for create/replace/update scenarios and create operation is allowed to be done by anonymous users.

Using just AllowAnonymous() without any arguments means all verbs are allowed anonymous access.

Other Authentication Providers

FastEndpoints supports all auth providers compatible with the ASP.NET middleware pipeline can be registered and used like above.

Here’s an example project using Auth0 with permissions.

--

--

Abdullah Ilgaz

Muslim • Enthusiast • Serverless Developer • Code Artisan • MVP • fullstack, serverless, aws, dotnet, typescript, reactjs, nextjs