0

For a work project, we have a central application named APC that handles user management for our internal applications; it allows for standard CRUD operations on users, applications, permissions, and roles.

This application currently uses Active Directory for authentication, using Windows Identity to handle claims. It provides a DLL containing a custom authorization attribute that we use in internal applications.

The business wants APC to be able to handle non-domain authentication (username and password) and they would also like to be able to support mobile devices. To that end, we've decided to make our application directly handle authentication as well by making it an OAuth provider; we'll expand our user persistence to handle usernames and passwords in addition to domain logins, and provide bearer tokens to support multiple application types.

Through my research, I've found good articles for using OWIN to create an OAuth provider with basic authentication, such as this one. I've also found Microsoft's Active Directory Authentication Library (ADAL), which apparently will handle the OAuth portion. However, I'm not sure how to marry the two authentication types in our provider. (This is compounded by the fact that while I understand authentication, I'm not that familiar with how .NET applications work with Active Directory. Generally, it's a tag in web.config, and it just works - I don't really know how to tap into that pipeline.)

How can I authenticate using Active Directory with a username and password combination as a fallback?

Here's what I've got so far, for reference. It's not much, unfortunately:

public override async Task GrantResourceOwnerCredentials( OAuthGrantResourceOwnerCredentialsContext context)
{
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); // TODO: Revisit.

    /* The authentication portion goes here.
    /* Check Active directory first. If that does not succeed, attempt basic authentication.
     */

    // Using Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext().AcquireToken can't work, as I need to generate my token based on the AD credentials.

    /* If AD fails, fall back to basic auth if it has been provided.
     */

    // If auth fails, set context error.
    if (false) // TODO: Revisit.
    {
        context.SetError("invalid_grant", "Authentication failed.");
    }

    // add user and app guid claims
    var identity = new ClaimsIdentity(context.Options.AuthenticationType);
    identity.AddClaim(new Claim(ApcClaimTypes.User, userGuid)); // TODO: Revisit.
    identity.AddClaim(new Claim(ApcClaimTypes.Application, appGuid)); // TODO: Revisit.

    // build claims from apc
    var apcClaims = new List<Claim>();

    // get privileges
    apcClaims.AddRange(_appService.GetPrivileges(uid, aid)
        .Select(p => new Claim(ApcClaimTypes.Privilege, p.PrivilegeCode)));

    // get roles
    apcClaims.AddRange(_appService.GetRoles(uid, aid)
        .Select(r => new Claim(ClaimTypes.Role, r.RoleCode)));

    identity.AddClaims(apcClaims);

    context.Validated(identity);
}
  • you can authenticate it easily using `PrincipalContext` tons of simple working examples online to help you do this.. – MethodMan Jan 06 '16 at 19:00
  • @MethodMan Sorry for the ignorance - but how do I get the actual credentials? If someone's already logged into the domain, I'm not sure how that's going to come through the `OAuthGrantResourceOwnerCredentialsContext` object. With username/password authentication, I call it manually and that's fine - the credentials end up in the context (as `username` and `password` credentials, respectively) and I can query my DB based on that. WIth AD authentication, I'm not sure. There's probably something simple I'm missing. –  Jan 06 '16 at 21:52
  • if you want to check against AD then use PrincipalContext I will post an example on how to know if they are in AD or not one sec – MethodMan Jan 06 '16 at 21:54
  • Of note - I didn't mention this but should have - a user can have an AD account and a basic account, and they could have different usernames and passwords for each authentication type. So it's not -quite- a fallthrough. This is why I need to know how the domain credentials and logon methodologies work (especially if someone is already logged into the domain when they access the OAuth endpoint). Hope that made sense. –  Jan 06 '16 at 21:56
  • I am showing you a simple way of doing this based on if someone is logged into the network.. still if they have different user and password in AD the same principal / theory should work.. test it out and you will see what I mean.. for example if your user name is `jahyoung` and it works then change it to `x12234~~` and it will fail then you will see what I am talking about.. – MethodMan Jan 06 '16 at 22:02
  • here is another posting I think that may help you with what you're trying to do using `OAuth` http://stackoverflow.com/questions/25071817/owin-bearer-token-authentication-authorize-controller – MethodMan Jan 06 '16 at 22:06

1 Answers1

0

you can do something like the following

using System.Security;
using System.DirectoryServices.AccountManagement;
public struct Credentials
{
    public string Username;
    public string Password;
}
public class Domain_Authentication
{
    public Credentials Credentials;
    public string Domain;
    public Domain_Authentication(string Username, string Password, string SDomain)
    {
        Credentials.Username = Username;
        Credentials.Password = Password;
        Domain = SDomain;
    }
    public bool IsValid()
    {
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
        {
            // validate the credentials
            return pc.ValidateCredentials(Credentials.Username, Credentials.Password);
        }
    }
}

or a simple test you can do this to see if the user is in AD as well

string fullName = null;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
    using (UserPrincipal user = UserPrincipal.FindByIdentity(context,"your domain user name"))
    {
        if (user != null)
        {
            fullName = user.DisplayName;
        }
    }
}
MethodMan
  • 18,625
  • 6
  • 34
  • 52