Search code examples
asp.net-coreauthorizationasp.net-core-security

Specifying Windows AuthenticationScheme and Roles Doesn't Work


How do I specify that the AuthenticationScheme is Windows and check that the user is a member of an AD Group?

When I specify the AuthenticationScheme, setting the Roles no longer works, why not? And how do I fix that?

public class SomeController : Controller
{
    //this works
    [Authorize(Roles = @"SOME.DOMAIN\SOME GROUP")]
    public IActionResult SomeAction(){ ... }

    //this works
    [Authorize(AuthenticationSchemes = "Windows")]

    //this doesn't work
    //[Authorize(Roles = @"SOME.DOMAIN\SOME GROUP",
    //   AuthenticationSchemes = "Windows")]
    public ActionResult SomeAction2(){ ... }
}

Full sample on GitHub


Some background

We have an AD Group called SOME GROUP that the user must be a member of to execute certain actions. In other parts of the web app, we're using cookie auth so I need to specify the authentication method in this particular controller.

Reference: Authorize with a specific scheme in ASP.NET Core


Solution

  • Turns out, the WindowsIdentity is preserved in the HttpContext.User object allowing us to check the group/role membership.

    Inline Example

    using System.Security.Principal;
    
    [Authorize(AuthenticationSchemes = IISServerDefaults.AuthenticationScheme)]
    public ActionResult SomeAction()
    {
        var windowsIdentity = HttpContext.User.Identity as WindowsIdentity;
        var windowsUser = new WindowsPrincipal(windowsIdentity);
        var role = "[MY-COMPUTER-NAME || AD GROUP NAME]\\[GROUP NAME]";
        var inInRole = windowsUser.IsInRole(role);
    
        // todo: if not allowed, write code to handle it
        
        return View();
    }
    

    Full source


    Policy Example

    //AuthorizationHandler<T>
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
    {
        if (!(context.User.Identity is WindowsIdentity windowsIdentity))
            return Task.CompletedTask;
    
        var windowsUser = new WindowsPrincipal(windowsIdentity);
        try
        {
            var hasRole = windowsUser?.IsInRole(requirement.GroupName) ?? false;
            if (hasRole)
                context.Succeed(requirement);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Unable to check groups the user belongs too");
        }
    
        return Task.CompletedTask;
    }
    
    //IAuthorizationRequirement
    public class RoleRequirement : IAuthorizationRequirement
    {
        public RoleRequirement(string groupName)
        { GroupName = groupName; }
    
        /// <summary>
        /// The Windows / AD Group Name that is allowed to call the OMS API
        /// </summary>
        public string GroupName { get; }
    }
    
    //action protected with the policy
    [Authorize("Super User Role")]
    public IActionResult Contact()
    { return View(); }
    
    //startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
        //pull group name from the config
        var securityOptions = Configuration.GetSection("Security").Get<SecurityOptions>();
    
        services.AddAuthentication(IISDefaults.AuthenticationScheme);
        services.AddAuthorization(options =>
        {
            options.AddPolicy("Super User Role", policy =>
            {
                policy.Requirements.Add(new RoleRequirement(securityOptions.AllowedGroup));
                policy.AddAuthenticationSchemes("Windows");
            });
        });
        services.AddSingleton<IAuthorizationHandler, RoleHandler>();
        // ...
    }
    

    Full Source