I have a small C# MVC5 app that I'm building and am ready to add the user security module to it. (previously I just created a session variable for testing roles) However, my security needs do not fit any of the prebuilt security modules that I've seen, i.e. SimpleMembership etc.
Here's a summary of my situation and needs:
No passwords -- username/password auth is not allowed
Users are authenticated at the web server level using a smartcard with client certificate -- I do not manage the server and will never see their card
IIS populates the Request.ClientCertificate and a few others, this is all I have access to
My app will have dedicated user accounts -- not using Windows auth etc -- so more like a username/password system without ever entering a username or password
The server is authenticating them to access the server by using some form of Windows auth with their smart card certificate, but I can't, I just have to accept the certificate loaded into the Request collection
User tables are stored in SQL Server (for now...)
Prefer not to use EntityFramework, since all app data comes from an Oracle DB and trying to get approval to move authentication/authorization tables into it and eliminate working from two DBs, and EF for Oracle doesn't work in our environment, so I'm using OleDb instead :(
What is the best way to go about implementing a scheme like this? What I've started doing is building three tables -- Users, Roles, and UserRoles -- and the Users table holds a copy of the (string) ClientCertificate. Users who come in would be authenticated into the app by pulling the Request.ClientCertificate and looking for a matching Users record, then getting the list of roles from UserRoles. A user object would be stored in the session containing the user and role info, and attributes would be used on controllers to control access by requiring certain roles.
(we have another app that uses this basic approach, but it is J2EE on Linux so I can't just reuse its code)
However, I've also started reading about IIdentity and IPrincipal but I'm not 100% clear on whether or not that is something I can use. Clearly I'd prefer to use a security model designed by experts. So should I build my authentication system using custom classes that inherit from IIdentity and IPrincipal? Or is there some other approach I should use?
It is entirely possible that something like SimpleMembership can be customized to meet my needs, but if so I'm not aware of it.
Thanks for the advice, much appreciated.
You could use Microsoft's Identity for this kind of advanced scenarios as well. Identity so modular you could use any data storage with any schema you want. Password for authentication in Identity is not necessary you could implement your own scenario. consider this simple example:
// imaging this action is called after user authorized by remote server
public ActionResoult Login()
{
// imaging this method gets authorized certificate string
// from Request.ClientCertificate or even a remote server
var userCer=_certificateManager.GetCertificateString();
// you have own user manager which returns user by certificate string
var user=_myUserManager.GetUserByCertificate(userCer);
if(user!=null)
{
// user is valid, going to authenticate user for my App
var ident = new ClaimsIdentity(
new[]
{
// since userCer is unique for each user we could easily
// use it as a claim. If not use user table ID
new Claim("Certificate", userCer),
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, userCer),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
// an optional claim you could omit this
new Claim(ClaimTypes.Name, user.Name),
// populate assigned user's role form your DB
// and add each one as a claim
new Claim(ClaimTypes.Role, user.Roles[0].Name),
new Claim(ClaimTypes.Role, user.Roles[1].Name),
// and so on
},
DefaultAuthenticationTypes.ApplicationCookie);
// Identity is sign in user based on claim don't matter
// how you generated it Identity take care of it
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
// auth is succeed, without needing any password just claim based
return RedirectToAction("MyAction");
}
// invalid certificate
ModelState.AddModelError("", "We could not authorize you :(");
return View();
}
As you can see we authorized user and populated roles without any dependency to username, password and any data storage since we used our own user manager.
Some usage example:
[Authorize]
public ActionResult Foo()
{
}
// since we injected user roles to Identity we could do this as well
[Authorize(Roles="admin")]
public ActionResult Foo()
{
// since we injected our authentication mechanism to Identity pipeline
// we have access current user principal by calling also
// HttpContext.User
}
This is a simple example you could implement your custom scenario extend IIdenity
as well. Read my other similar answers like this and this for more examples to see how you can do almost everything by Claims
.
Also you could browse and download Token Based Authentication Sample repo as a simple working example.
From the sounds of it the user is already authenticated, as such in your controller you can simply access the User
property on Controller
base class that your controllers inherit. This is property returns an IIPrincipal
which has a property called Identity
which returns an IIdentity
.
So for example if you wanted to retrieve the currently logged in user's username in your controller you would access User.Identity.Name
or if you wanted to ensure that the user was logged in you could check User.Identity.IsAuthenticated
.
You absolutely should not be pulling anything out of the client certificates, essentially the server should have already authenticated the user, you don't care that it was done via a certificate rather than a username/password.