Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Custom authorization policies with
Use the IAuthorizationRequirementData interface to specify requirements associated with the authorization policy in attribute definitions.
Sample app
The complete sample described in this article is the AuthRequirementsData sample app (dotnet/AspNetCore.Docs.Samples
GitHub repository) (how to download). The sample app implements a minimum age handler for users, requiring a user to present a birth date claim indicating that they're at least 21 years old.
Minimum age authorize attribute
The MinimumAgeAuthorizeAttribute
implementation of IAuthorizationRequirementData sets an authorization age:
using Microsoft.AspNetCore.Authorization;
namespace AuthRequirementsData.Authorization;
class MinimumAgeAuthorizeAttribute(int age) : AuthorizeAttribute,
IAuthorizationRequirement, IAuthorizationRequirementData
{
public int Age { get; set; } = age;
public IEnumerable<IAuthorizationRequirement> GetRequirements()
{
yield return this;
}
}
Minimum age authorization handler
The MinimumAgeAuthorizationHandler
class handles the single IAuthorizationRequirement provided by MinimumAgeAuthorizeAttribute
, as specified by the generic parameter MinimumAgeAuthorizeAttribute
.
The HandleRequirementAsync
method:
- Gets the user's birth date claim.
- Obtains the user's age from the claim.
- Adjusts age if the user hasn't had a birthday this year.
- Marks the authorization requirement succeeded if the user meets the age requirement.
- Implements logging for demonstration purposes.
using System.Globalization;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
namespace AuthRequirementsData.Authorization;
class MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger)
: AuthorizationHandler<MinimumAgeAuthorizeAttribute>
{
// Check whether a given minimum age requirement is satisfied.
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeAuthorizeAttribute requirement)
{
logger.LogInformation(
"Evaluating authorization requirement for age >= {age}",
requirement.Age);
// Get the user's birth date claim.
var dateOfBirthClaim =
context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);
if (dateOfBirthClaim != null)
{
// If the user has a date of birth claim, obtain their age.
var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value,
CultureInfo.InvariantCulture);
var age = DateTime.Now.Year - dateOfBirth.Year;
// Adjust age if the user hasn't had a birthday yet this year.
if (dateOfBirth > DateTime.Now.AddYears(-age))
{
age--;
}
// If the user meets the age requirement, mark the authorization
// requirement succeeded.
if (age >= requirement.Age)
{
logger.LogInformation(
"Minimum age authorization requirement {age} satisfied",
requirement.Age);
context.Succeed(requirement);
}
else
{
logger.LogInformation(
"Current user's DateOfBirth claim ({dateOfBirth}) doesn't " +
"satisfy the minimum age authorization requirement {age}",
dateOfBirthClaim.Value,
requirement.Age);
}
}
else
{
logger.LogInformation("No DateOfBirth claim present");
}
return Task.CompletedTask;
}
}
The MinimumAgeAuthorizationHandler
is registered as a singleton IAuthorizationHandler service in the app's Program
file:
builder.Services.AddSingleton<IAuthorizationHandler,
MinimumAgeAuthorizationHandler>();
The GreetingsController
displays the user's name when they satisfy the minimum age policy, using an age of 21 years old with the [MinimumAgeAuthorize({AGE})]
attribute, where the {AGE}
placeholder is the age:
using Microsoft.AspNetCore.Mvc;
using AuthRequirementsData.Authorization;
namespace AuthRequirementsData.Controllers;
[ApiController]
[Route("api/[controller]")]
public class GreetingsController : Controller
{
[MinimumAgeAuthorize(21)]
[HttpGet("hello")]
public string Hello() =>
$"Hello {HttpContext.User.Identity?.Name}!";
}
If the user's birth date claim indicates that they're at least 21 years old, the controller displays the greeting string, issuing a 200 (OK) status code. If the user is missing the birth date claim or the claim indicates that they aren't at least 21 years old, the greeting isn't displayed and a 403 (Forbidden) status code is issued.
JWT bearer authentication services are added in the app's Program
file:
builder.Services.AddAuthentication().AddJwtBearer();
The app settings file (appsettings.json
) configures the audience and issuer for JWT bearer authentication:
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [
"https://localhost:51100"
],
"ValidIssuer": "dotnet-user-jwts"
}
}
}
In the preceding example, the localhost audience matches the localhost address specified by applicationUrl
in the app's launch profile (Properties/launchSettings.json
).
Demonstration
Test the sample with dotnet user-jwts
and curl.
From the project's folder in a command shell, execute the following command to create a JWT bearer token with a birth date claim that makes the user over 21 years old:
dotnet user-jwts create --claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=1989-01-01
The output produces a token after "Token:
" in the command shell:
New JWT saved with ID '{JWT ID}'.
Name: {USER}
Custom Claims: [http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=1989-01-01]
Token: {TOKEN}
Set the value of the token (where the {TOKEN}
placeholder appears in the preceding output) aside for use later.
You can decode the token in an online JWT decoder, such as jwt.ms
to see its contents, revealing that it contains a birthdate
claim with the user's birth date:
{
"alg": "HS256",
"typ": "JWT"
}.{
"unique_name": "{USER}",
"sub": "{USER}",
"jti": "{JWT ID}",
"birthdate": "1989-01-01",
"aud": [
"https://localhost:51100",
"http://localhost:51101"
],
"nbf": 1747315312,
"exp": 1755264112,
"iat": 1747315313,
"iss": "dotnet-user-jwts"
}.[Signature]
Execute the command again with a dateofbirth
value that makes the user under the age of 21:
dotnet user-jwts create --claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=2020-01-01
Set the value of second token aside.
Start the app in Visual Studio or with the dotnet watch
command in the .NET CLI.
In the .NET CLI, execute the following curl.exe
command to request the api/greetings/hello
endpoint. Replace the {TOKEN}
placeholder with the first JWT bearer token that you saved earlier:
curl.exe -i -H "Authorization: Bearer {TOKEN}" https://localhost:51100/api/greetings/hello
The output indicates success because the user's birth date claim indicates that they're at least 21 years old:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Thu, 15 May 2025 22:58:10 GMT
Server: Kestrel
Transfer-Encoding: chunked
Hello {USER}!
Logging indicates that the age requirement was met:
MinimumAgeAuthorizationHandler: Information: Evaluating authorization requirement for age >= 21
MinimumAgeAuthorizationHandler: Information: Minimum age authorization requirement 21 satisfied
Re-execute the curl.exe
command with the second token, which indicates the user is under 21 years old. The output indicates that the requirement isn't met. Access to the endpoint is forbidden (status code 403):
HTTP/1.1 403 Forbidden
Content-Length: 0
Date: Thu, 15 May 2025 22:58:36 GMT
Server: Kestrel
MinimumAgeAuthorizationHandler: Information: Evaluating authorization requirement for age >= 21
MinimumAgeAuthorizationHandler: Information: Current user's DateOfBirth claim (2020-01-01) doesn't satisfy the minimum age authorization requirement 21
ASP.NET Core