.NET Package that allows developers configure Authorization in a fast way only providing the Identity Provider to use.
$ dotnet add package Security.Authz.ApiSecurity.Authz.Api is a library with features focused on integrating .NET Applications with Identity Providers such as Keycloak and Entra ID.
You can specify which of them implement in your application and even the RoleClaimsTransformation to use.
Add the library to your project using the .NET CLI:
dotnet add package Security.Authz.Api
Configure your Environment Values or Secrets:
You have to define the following section in your appsettings.json file
"Authentication": {
"Authority": "<<YOUR_IDP_AUTHORITY>>",
"Issuer": "<<ISSUER>>", // Get it in the Metadata URL
"Audience": "<<AUD_CONFIGURED_FOR_YOUR_CLIENT>>",
"ClientId": "<<YOUR_CLIENT_ID>>",
"ClientSecret": "<<YOUR_CLIENT_SECRET>>",
"NameClaimType": "<<NAME_CLAIM_TO_IDENTIFY_THE_USER>>", // e.g.: When using Keycloak you can use the value preferred_username
"RolesClaim": "resource_access.<<YOUR_AUD>>" // This field is optional. See more information about it in the Topic How is the interation with the roles
"RequireRoles": [
"First.Role",
"Second.Role"
],
"RequireHttpsMetadata": true,
"ValidateIssuerSigningKey": true,
"ValidateLifetime": true,
"ValidateAudience": true,
"ValidateIssuer": true
}
Configure the authentication in your application:
// On your Program.cs file
using Security.Authz.Api;
...
builder.Services.ConfigureAuthentication(builder.Configuration, builder.Environment, IdentityProvider.Keycloak);
IMPORTANT
The IdP to be configured will be specified by the Enum Value passed as the Third Param on the Extension Method ConfigureAuthentication.
This topic is specially dedicated to the Handle of Roles, this involves: Definitions, Usage with the Library, and so on.
Basically, the roles can be with different presentations according to the Definitions and the IdP been used. So, with that been said there are some things to have in mind.
Imagine your JWT has the roles like the following example:
{
...
"roles": [
"Org.Admin",
"Org.AnotherOne"
]
...
}
With this structure the roles will be completely tolerated and handled by the package without any other configuration than specifying the roles to consider in the application (field RequireRoles in your appsettings.json file).
But, if you've defined any other Claim you have to specify it using the property RoleClaim in your appsettings.json file. Imagine you've configured a mapper and the JSON is something like the following example:
{
...
"custom_claim": {
"another_field": {
"roles": [
"Org.Admin",
"Org.AnotherOne"
]
}
}
...
}
So, you have to specify it like this:
{
"Authentication": {
...
"RolesClaim": "custom_claim.another_field"
...
}
}
This topic is related to the property RequireRoles, and it's because is good to mention that with the list of roles configured with this field the User (executing the request) only has to have at least one of them.
But, what if you want to do something different? like establish that to execute an endpoint the user must has two roles or one in specific. Or maybe you want to allow Public Execution of your endpoints (without previous authentication).
With .NET we have a decorator named Authorize and with it we can define one role or more to be required for a specific endpoint or an entire controller:
// For an entire controller
[Authorize(Roles = "Admin")]
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
// ... actions only accessible by "Admin" role
}
// For specific methods
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("user-profile")]
[Authorize(Roles = "User, Admin")] // Accessible by "User" or "Admin"
public IActionResult GetUserProfile() { /* ... */ }
[HttpPost("create-report")]
[Authorize(Roles = "Admin")] // Only accessible by "Admin"
public IActionResult CreateReport() { /* ... */ }
}
If you configure all the things all your application will be protected by the rule that the User must has at least one of the roles. But if you want to allow that a functionality can be executed without any JWT (without previous authentication), you only have to use a decorator named AllowAnonymous.
// For an entire controller
[AllowAnonymous]
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("public-data")]
public IActionResult GetPublicData() { /* ... */ }
[HttpGet("user-profile")]
public IActionResult GetUserProfile() { /* ... */ }
[HttpPost("create-report")]
public IActionResult CreateReport() { /* ... */ }
}
// For specific methods
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("public-data")]
[AllowAnonymous] // Anyone can access this
public IActionResult GetPublicData() { /* ... */ }
[HttpGet("user-profile")]
[Authorize(Roles = "User, Admin")] // Accessible by "User" or "Admin"
public IActionResult GetUserProfile() { /* ... */ }
}
Contributions are welcome! Please fork the repository and submit a pull request