Specifying Windows AuthenticationScheme and Roles

2019-08-25 09:11发布

问题:

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

回答1:

Windows authentication is different to every other authentication handler. ASP.NET doesn't do the authentication, a windows component does, and passes ASP.NET Core a handle to the identity it created. It's not designed for, or meant to be mixed with other authentication types, it's either Windows and Anonymous, or just Windows.

Mixing it with anything else is unsupported, so you shouldn't ever need to limit by scheme, even if it did work.



回答2:

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