By Mike Rousos
Typically when using policy-based authorization, policies are registered by calling
AuthorizationOptions.AddPolicy as part of authorization service configuration. In some scenarios, it may not be possible (or desirable) to register all authorization policies in this way. In those cases, you can use a custom
IAuthorizationPolicyProvider to control how authorization policies are supplied.
Examples of scenarios where a custom IAuthorizationPolicyProvider may be useful include:
- Using an external service to provide policy evaluation.
- Using a large range of policies (for different room numbers or ages, for example), so it doesn’t make sense to add each individual authorization policy with an
- Creating policies at runtime based on information in an external data source (like a database) or determining authorization requirements dynamically through another mechanism.
View or download sample code from the AspNetCore GitHub repository. Download the aspnet/AspNetCore repository ZIP file. Unzip the file. Navigate to the src/Security/samples/CustomPolicyProvider project folder.
Customize policy retrieval
ASP.NET Core apps use an implementation of the
IAuthorizationPolicyProvider interface to retrieve authorization policies. By default, DefaultAuthorizationPolicyProvider is registered and used.
DefaultAuthorizationPolicyProvider returns policies from the
AuthorizationOptions provided in an
You can customize this behavior by registering a different
IAuthorizationPolicyProvider implementation in the app’s dependency injection container.
IAuthorizationPolicyProvider interface contains two APIs:
- GetPolicyAsync returns an authorization policy for a given name.
- GetDefaultPolicyAsync returns the default authorization policy (the policy used for
[Authorize]attributes without a policy specified).
By implementing these two APIs, you can customize how authorization policies are provided.
Parameterized authorize attribute example
One scenario where
IAuthorizationPolicyProvider is useful is enabling custom
[Authorize] attributes whose requirements depend on a parameter. For example, in policy-based authorization documentation, an age-based (“AtLeast21”) policy was used as a sample. If different controller actions in an app should be made available to users of different ages, it might be useful to have many different age-based policies. Instead of registering all the different age-based policies that the application will need in
AuthorizationOptions, you can generate the policies dynamically with a custom
IAuthorizationPolicyProvider. To make using the policies easier, you can annotate actions with custom authorization attribute like
Custom Authorization Attributes
Authorization policies are identified by their names. The custom
MinimumAgeAuthorizeAttribute described previously needs to map arguments into a string that can be used to retrieve the corresponding authorization policy. You can do this by deriving from
AuthorizeAttribute and making the
Age property wrap the
internal class MinimumAgeAuthorizeAttribute : AuthorizeAttribute const string POLICY_PREFIX = "MinimumAge"; public MinimumAgeAuthorizeAttribute(int age) => Age = age; // Get or set the Age property by manipulating the underlying Policy property public int Age get if (int.TryParse(Policy.Substring(POLICY_PREFIX.Length), out var age)) return age; return default(int); set Policy = $"POLICY_PREFIXvalue.ToString()";
This attribute type has a
Policy string based on the hard-coded prefix (
"MinimumAge") and an integer passed in via the constructor.
You can apply it to actions in the same way as other
Authorize attributes except that it takes an integer as a parameter.
[MinimumAgeAuthorize(10)] public IActionResult RequiresMinimumAge10()
MinimumAgeAuthorizeAttribute makes it easy to request authorization policies for any minimum age desired. The next problem to solve is making sure that authorization policies are available for all of those different ages. This is where an
IAuthorizationPolicyProvider is useful.
MinimumAgeAuthorizationAttribute, the authorization policy names will follow the pattern
"MinimumAge" + Age, so the custom
IAuthorizationPolicyProvider should generate authorization policies by:
- Parsing the age from the policy name.
AuthorizationPolicyBuilderto create a new
- Adding requirements to the policy based on the age with
AuthorizationPolicyBuilder.AddRequirements. In other scenarios, you might use
internal class MinimumAgePolicyProvider : IAuthorizationPolicyProvider const string POLICY_PREFIX = "MinimumAge"; // Policies are looked up by string name, so expect 'parameters' (like age) // to be embedded in the policy names. This is abstracted away from developers // by the more strongly-typed attributes derived from AuthorizeAttribute // (like [MinimumAgeAuthorize()] in this sample) public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase) && int.TryParse(policyName.Substring(POLICY_PREFIX.Length), out var age)) var policy = new AuthorizationPolicyBuilder(); policy.AddRequirements(new MinimumAgeRequirement(age)); return Task.FromResult(policy.Build()); return Task.FromResult<AuthorizationPolicy>(null);
Multiple authorization policy providers
When using custom
IAuthorizationPolicyProvider implementations, keep in mind that ASP.NET Core only uses one instance of
IAuthorizationPolicyProvider. If a custom provider isn’t able to provide authorization policies for all policy names, it should fall back to a backup provider. Policy names might include those that come from a default policy for
[Authorize] attributes without a name.
For example, consider an application needed both custom age policies and more traditional role-based policy retrieval. Such an app could use a custom authorization policy provider that:
- Attempts to parse policy names.
- Calls into a different policy provider (like
DefaultAuthorizationPolicyProvider) if the policy name doesn’t contain an age.
In addition to providing named authorization policies, a custom
IAuthorizationPolicyProvider needs to implement
GetDefaultPolicyAsync to provide an authorization policy for
[Authorize] attributes without a policy name specified.
In many cases, this authorization attribute only requires an authenticated user, so you can make the necessary policy with a call to
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
As with all aspects of a custom
IAuthorizationPolicyProvider, you can customize this, as needed. In some cases:
- Default authorization policies might not be used.
- Retrieving the default policy can be delegated to a fallback
Use a custom IAuthorizationPolicyProvider
To use custom policies from an
IAuthorizationPolicyProvider, you must:
- Register the appropriate
AuthorizationHandlertypes with dependency injection (described in policy-based authorization), as with all policy-based authorization scenarios.
- Register the custom
IAuthorizationPolicyProvidertype in the app’s dependency injection service collection (in
Startup.ConfigureServices) to replace the default policy provider.
A complete custom
IAuthorizationPolicyProvider sample is available in the aspnet/AuthSamples GitHub repository.