question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve custom IAuthorizeData with IAuthorizationRequirementsProvider

See original GitHub issue

Currently, in AspNetCore 3.1, the middleware AuthorizationMiddleware calls the AuthorizationPolicy.CombineAsync(IAuthorizationPoliceProvider, IEnumerable<IAuthorizeData>) static method to build a unified authorization policy that combines the rules defined in the authorization metadata of instance type IAuthorizeData (ie: AuthorizeAttribute). All the rules translated from the IAuthorizeData metadata is made by this method.

In a simple way, the CombineAsync method:

  • combines the policies defined by IAuthorizeData.Policy property;
  • adds a RolesAuthorizationRequirement instance to this unified policy if the IAuthorizeData.Roles property is set;
  • combines the defined authorization schemes by the IAuthorizeData.AuthorizationSchemes property.

The policy-based authorization approach aggregates the policy validation rules by a set of IAuthorizationRequirement objects. So, to improve that approach and to facilitate the custom implementations of IAuthorizeData types, I suggest some aspects that can benefit the implementation of authorization in AspNetCore.

  1. Provide an interface with the following contract:

    interface IAuthorizationRequirementsProvider
    {
        Task<IEnumerable<IAuthorizationRequirement>> GetRequirementsAsync(IAuthorizeData authorizeData);
    }
    

    That’s interface method takes an IAuthorizeData instance and returns the IAuthorizationRequirement instances relevant to your authorization logic.

    • Multiple IAuthorizationRequirementsProvider providers can be registered in the service container.
    • Two default providers will be made available and registered by the framework:
      • RolesAuthorizationRequirementsProvider: which implements the current logic in CombineAsync;

      • PassThroughAuthorizationRequirementsProvider: which behaves similarly to the PassThroughAuthorizationHandler class:

        async Task<IEnumerable<IAuthorizationRequirement>> GetRequirementsAsync(IAuthorizeData authorizeData)
        {
            if (authorizeData is IAuthorizationRequirementsProvider requirementsProvider)
            {
                return await requirementsProvider.GetRequirementsAsync(authorizeData);
            }
        
            if (authorizeData is IAuthorizationRequirement requirement)
            {
                return new[] { requirement };
            }
        
            return await AuthorizationRequirementsProvider.EmptyResult;
        }
        
  2. The CombineAsync method will be relocated to the IAuthorizationPolicyProvider interface, with the signature Task<AuthorizationPolicy> CreatePolicyAsync(IEnumerable<IAuthorizeData> authorizeData).

    • The CreatePolicyAsync code, implemented in DefaultAuthorizationPolicyProvider, is the same as CombineAsync, with a slight difference:

      • The code block:
        var rolesSplit = authorizeDatum.Roles?.Split(',');
        if (rolesSplit?.Length > 0)
        {
            var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
            policyBuilder.RequireRole(trimmedRolesSplit);
            useDefaultPolicy = false;
        }
        
      • Becomes:
        // _requirementsProviders: IEnumerable<IAuthorizationRequirementsProvider>
        // injected in constructor
        foreach (var requirementsProvider in _requirementsProviders)
        {
            var requirements = await requirementsProvider.GetRequirementsAsync(authorizeDatum);
            var requirementsArray = requirements as IAuthorizationRequirement[] ?? requirements.ToArray();
        
            if (requirementsArray.Length > 0)
            {
                useDefaultPolicy = false;
            }
        
            policyBuilder.AddRequirements(requirementsArray);
        }
        
      • CombineAsync now calls:
      return await policyProvider.CreatePolicyAsync(authorizeData);
      
      • CombineAsync is marked as obsolete. In AspNetCore 5, this method can be removed.

A sample case:


public class SampleAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirementsProvider
{
    /// <summary>
    /// Gets or sets the scope allowed to access the resource.
    /// </summary>
    string Scope { get; set; }

    public Task<IEnumerable<IAuthorizationRequirement>> GetRequirementsAsync(IAuthorizeData authorizeData)
    {
        if (Scope == null)
            return AuthorizationRequirementsProvider.EmptyResult;

        return AuthorizationRequirementsProvider.Result(
            new ClaimsAuthorizationRequirement("scope", new[] { Scope })
        );
    }
}

Other implementations combining IAuthorizationRequirement and IAuthorizationHandler are covered.

For more details, see the affected code at: https://github.com/rodrigo-speller/aspnetcore/tree/authorization-requirements-provider/src/Security/Authorization/Core/src

  • AuthorizationPolicy.cs
  • AuthorizationRequirementsProvider.cs
  • AuthorizationServiceCollectionExtensions.cs
  • DefaultAuthorizationPolicyProvider.cs
  • IAuthorizationPolicyProvider.cs
  • IAuthorizationRequirementsProvider.cs
  • PassThroughAuthorizationRequirementsProvider.cs
  • RolesAuthorizationRequirementsProvider.cs

There are no breaking changes.


I’m requesting for comments before “pull request”.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
blowdartcommented, Jun 9, 2020

I’m putting this into discussion, because 5 is pretty much done

1reaction
kevinchaletcommented, Jun 9, 2020

Convincing @blowdart won’t be easy. We have already tried… https://github.com/aspnet/Security/issues/1689 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implement a Custom Authorization Policy Provider for ASP. ...
In this blog post we will implement a custom authorization policy provider for an ASP.NET Core application using Verified Permissions.
Read more >
Custom Authorization Policy Provider ... - Crafted For Everyone
This article explains implementing authorization by creating your own custom attribute or by creating Custom Authorization Policy Provider.
Read more >
Custom authorization policies with ...
Learn how to add custom authorization policies with IAuthorizationRequirementData.
Read more >
How do you create a custom AuthorizeAttribute in ASP.NET ...
I'm trying to make a custom authorization attribute in ASP.NET Core. In previous versions it was possible to override bool AuthorizeCore( ...
Read more >
How to Create a Custom Authorize Attribute in ASP.NET Core
Let's implement a custom authorization attribute using both IAuthorizationFilter and policy-based authorization.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found