Creating a new AuthorizationHandler/IAuthorizationRequirement that uses a service

Matthew picture Matthew · Jun 1, 2016 · Viewed 14.4k times · Source

I am trying to create a new Authorization requirement, but it must use one of the services that I declare in ConfigureServices, and I have no idea how to pass that service to the new requirement in the same method as declaring the service.

public class NewRequirement: AuthorizationHandler<NewRequirement>, IAuthorizationRequirement
{
    private IRepository _repository;

    public NewRequirement(IRepository repository)
    {
        _repository = repository;
    }

    protected override void Handle(AuthorizationContext context, NewRequirement requirement)
    {
        //code that uses _repository here
    }
}

This works fine, but when I try to add the policy in Startup.cs like this:

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddSingleton<RepositoryContext>();
        services.AddScoped<IRepository, Repository>();

        services.Configure<AuthorizationOptions>(options =>
        {
            options.AddPolicy("NewRequirement", policy => policy.Requirements.Add(new NewRequirement()));
        });
    }

I get this error

Error CS7036
There is no argument given that corresponds to the required formal parameter 'repository' of 'ExtraProjectRequirements.NewRequirement(IRepository)'
Reception.DNX 4.5.1

I think I need to pass the IRepository to the new policy but I have no idea how to do it in ConfigureServices.

Answer

Tseng picture Tseng · Jun 1, 2016

You are passing the handler to the requirement, which is wrong. IAuthorizationRequirement and AuthorizationHandler<NewRequirement> need to be two distinct classes. Also IAuthorizationRequirement is only a marker interface w/o any mandatory properties or methods, just there to accidentally adding arbitrary classes to the Requirements collection ;)

The IAuthorizationRequirement will contain pure data (reads: No services, no dependencies that need to be injected) required for your requirement, the handler will validate it. See @blowdart example of an Over18Requirement and it's handler as well as the official documentation.

Handlers are allowed to have dependencies injected.

Examples from the documentation for future readers (in case link becomes unavailable).

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int age)
    {
        MinimumAge = age;
    }

    protected int MinimumAge { get; set; }
}

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override void Handle(AuthorizationContext context, MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                   c.Issuer == "http://contoso.com"))
        {
            return;
        }

        var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(
            c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }
    }
}