简体   繁体   中英

Authentication with custom implementation of IAuthorizationPolicyProvider in dotnet core 3.1

I have implemented a custom IAuthorizationPolicyProvider following the documentation provided here , but when I debug and reach the handler and look into the context.User object, I see that the properties like IsAuthenticated or context.User.IsInRole are false/empty. My application is configured with jwt token authorization, and I have confirmed that the token does in fact contain values in the roles payload data, but it doesn't seem to authenticating before it reaches the handler for me to use those values. Can someone help me understand the order of operations, or how I might be able to step through the authentication actually happening?

I have both authentication and authorization in my Startup.cs:

services.AddAuthentication(sharedOptions =>
{
    sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})

services.AddAuthorization(options =>
{
    var defaultAuthorizationBuilder = new AuthorizationPolicyBuilder("Bearer");
    defaultAuthorizationBuilder = defaultAuthorizationBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationBuilder.Build();
}

services.AddSingleton<IAuthorizationPolicyProvider, MyCustomPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, MyCustomHandler>();

After much tinkering, I want to share what I learned that answers this and the broader question of how implementing a custom policy provider works. When using AddAuthentication and AddAuthorization in the Startup.cs, this serves as the policy setup that will be used when you set up the default authorization policy provider, for example like so:

public YourAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
{
    this.BackupPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}

For me, I use this fallback policy provider as the default:

public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
    return this.BackupPolicyProvider.GetDefaultPolicyAsync(); //this is the default policy established in Startup.cs
}

(Note: something I found confusing and not a lot of clear documentation on: DefaultPolicy vs FallbackPolicy, especially because it appears that, at the time of writing this, GetFallbackPolicyAsync recently became a method that requires implementation when implementing IAuthorizationPolicyProvider. DefaultPolicy: when the [Authorize] attribute is applied, this is the policy to be used. When no policy is provided, then GetFallbackPolicyAsync is called.)

What I was missing when I was building my custom policies was specifying in the AuthorizationPolicyBuilder which authentication scheme I wanted the policy to use. I realize now it's in the docs, but it's not called out specifically so I missed it:

public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
    if (//some check on your policy name)
    {
        var policy = new AuthorizationPolicyBuilder(//what scheme to use for authentication);
        // your requirements
        return Task.FromResult(policy.Build());
    }
}

The question & current answer gave me excellent direction on how to configure this myself. Sharing a full example of how a custom policy provider can be configured. In my case, I wanted to decorate actions with [AuthorizePermission("Read")] and have the permission checked by a custom AuthorizationHandler .

CustomPolicyProvider.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;

namespace MyApp.Authentication.Policies
{
    public class CustomPolicyProvider : IAuthorizationPolicyProvider
    {
        public DefaultAuthorizationPolicyProvider OriginalPolicyProvider { get; set; }

        public CustomPolicyProvider(
            IOptions<AuthorizationOptions> options)
        {
            OriginalPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
        }

        public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => OriginalPolicyProvider.GetDefaultPolicyAsync();
        public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => OriginalPolicyProvider.GetFallbackPolicyAsync();
        public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
        {
            if (policyName.StartsWith(AuthSettings.Policies.Permission, StringComparison.OrdinalIgnoreCase))
            {
                var permission = policyName[AuthSettings.Policies.Permission.Length..];
                if (!string.IsNullOrWhiteSpace(permission))
                {
                    var policy = new AuthorizationPolicyBuilder(AuthSettings.Schemes.All);

                    policy.AddRequirements(new PermissionRequirement(permission));

                    return Task.FromResult<AuthorizationPolicy?>(policy.Build());
                }
            }

            return OriginalPolicyProvider.GetPolicyAsync(policyName);
        }
    }
}

Program.cs

builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
builder.Services.AddAuthorization(options =>
{
    options.DefaultPolicy =  new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
                                                             .AddAuthenticationSchemes(AuthSettings.Schemes.All)
                                                             .Build();
    options.AddPolicy(AuthSettings.Policies.Scopes.General, policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.AddAuthenticationSchemes(AuthSettings.Schemes.All);
        policy.RequireScope("api");
    });
    options.AddPolicy(AuthSettings.Policies.Scopes.Identity, policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.AddAuthenticationSchemes(AuthSettings.Schemes.All);
        policy.RequireScope("api.identity");
    });
});
builder.Services.AddSingleton<IAuthorizationPolicyProvider, CustomPolicyProvider>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM