I've cobbled together some resources, and I decided to create a simple, custom authentication that allows Active Directory and individual user accounts in my database.
First, I added ASP.NET Identity to my existing project. I made the Identity interfaces much more simpler than in the linked answer:
IdentityConfig.cs
public class IdentityConfig
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(() => new Entities());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Authentication/Login"),
});
}
}
Following @Sam's answer (yet again) on creating custom authentication/authorization in ASP.NET, I then created a simple Database-First user and role table in the database, not based off of Identity, and created a user manager class:
UserManager.cs
public class UserManager
{
private Entities db = new Entities();
public bool IsValid(string username, string password)
{
// TODO: salt and hash.
return db.USER.Any(u => u.USERNAME == username && u.PASSWORD == password);
}
}
Finally, to complete the custom authentication, I created a dead simple authentication controller. That will check if the user is valid, then create a ClaimIdentity
.
AuthenticationController.cs
public class AuthenticationController : Controller
{
private Entities db = new Entities();
public ActionResult Login()
{
return View();
}
public ActionResult Logout()
{
HttpContext.GetOwinContext().Authentication.SignOut();
return RedirectToAction("Index", "Home");
}
[HttpPost]
public ActionResult Login(string username, string password)
{
UserManager um = new UserManager();
bool valid = um.IsValid(username, password);
if (valid)
{
// get user role and enditem
USER user = db.USER.Where(u => u.USERNAME == username).First();
string role = db.ROLE.Where(r => r.USERID == user.USERID).FirstOrDefault().ROLENAME;
// create session
Claim usernameClaim = new Claim(ClaimTypes.Name, username);
Claim roleClaim = new Claim(ClaimTypes.Role, role);
ClaimsIdentity identity = new ClaimsIdentity(
new[] { usernameClaim, roleClaim }, DefaultAuthenticationTypes.ApplicationCookie
);
// auth succeed
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = false }, identity);
return RedirectToAction("Index", "Home");
}
// invalid username or password
ViewBag.error = "Invalid Username";
return View();
}
}
This simplicity seems exactly what I want, not carrying the massive bulk that is Identity or ASP.NET membership, and it works great.
Now to answer my original question - how do I also accommodate Active Directory users?
While I abstracted out and greatly simplified my USER and ROLE classes, there will be significant extra data that will be required for a USER (roles, permissions, etc) - and that data will not be in Active Directory - we need to create a USER regardless.
Therefore, all I need is to validate the username and password in Active Directory!
A quick configuration variable can change the control flow in the Login
action, and execute this:
bool isValid = false;
if (authConfig == "AD") {
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "US"))
{
// validate the credentials
isValid = pc.ValidateCredentials(username, password);
}
} else if (authConfig == "Custom") {
isValid = um.IsValid(username, password);
}
// create claim...
This solution was possible because the only purpose of Active Directory authentication is to verify their user - no additional data was required from AD. Additionally, since we need to specify Role and other data in our custom tables, a custom user record had to be created anyway.
My confusion that spurred the answer was the lack of understanding of how flexible it is to create custom authentication in .NET, without using what they have baked in (like checking the "Individual User Accounts" option when creating a new project).
Other suggestions are welcomed, since this is the extent of my ASP.NET knowledge - but I believe this will work for my application. I hope this helps someone's authentication setup.