Have you ever been in the start of a project where there is no agreement about how to authenticate your users? Maybe you are in discussions about using ASP.NET Identity with Owin authentication middleware, maybe the Membership provider, maybe ADFS or Identity Server.
There are many options, and sometimes because of that (and because security is hard) this is done at the end of projects, frequently under time pressure which is never good.
Turns out that what the way you do access control in your ASP.NET MVC app is pretty much independent of how you decide to authenticate and authorize your users.
You just have to create an IPrincipal and an IIdentity and set it up in HttpContext (see this for a simple example).
Even though you don’t need to choose the authentication mechanism to have authenticated users, you do have to pick the implementation of IPrincipal and IIdentity that you want to use.
You should pick ClaimsPrincipal and ClaimsIdentity.
The reason for this is that if you embrace Claims Based Authentication you will be assigning claims to your users. If you don’t know about claims (and you are used to use roles in your applications), this is the best explanation I’ve seen.
Oh, and another reason, there’s a claim type (a claim has among other things a type and a value) that you can use that will behave exactly as a Role (AuthorizeAttribute will treat it just as you would expect, e.g. [Authorize(Role=”Admin)]). That claim type is… you’ve guessed it, Role (System.Security.Claims.ClaimTypes.Role). So if you add claims with that type to your ClaimsIdentity, it would be just as if the user contains the roles that are those claims’ values.
So if you want to have users who you can configure without actually using an authentication mechanism you can:
- Read from a configuration file (web.config would be the easiest place) the properties that you want your user to have (e.g. it’s name, roles, etc)
Handle the Authenticate event in Global.asax (this will work even if you are using Owin/Katana and hosting in IIS, or you can create a custom Owin middleware that will work just as well as doing it in Global.asax)- In the handler, create a ClaimsPrincipal with a ClaimsIdentity and set the principal to HttpContext.Current.User
If you decide to store the user information as app settings entries in your web.config, it would look like this:
<configuration>
<appSettings>
<add key="test-security" value="true"/>
<add key="username" value="John Doe"/>
<add key="roles" value="Manager Admin"/>
</appSettings>
And your Global.asax would look like this:
public class MvcApplication : System.Web.HttpApplication
{
public MvcApplication()
{
AuthenticateRequest += OnAuthenticateRequest;
}
private void OnAuthenticateRequest(object sender, System.EventArgs e)
{
if (ConfigurationManager.AppSettings["test-security"] != "true")
return;
var username = ConfigurationManager.AppSettings["username"];
var roles = ConfigurationManager.AppSettings["roles"].Split(' ');
ClaimsIdentity identity = new ClaimsIdentity(authenticationType: "test-security");
identity.AddClaim(new Claim(ClaimTypes.Name, username));
roles.ToList().ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
HttpContext.Current.User = principal;
}
This will “login” a user with username “John Doe” and the roles “Admin” and “Manager”.
You can find this example on github here.
NOTE: You have to set an authenticationType (in the example above it’s “test-security”) when you create the ClaimsIdentity or else the property IIdentity.IsAuthenticated will return false.
Owin
If you are using Owin you don’t need Global.asax. You can just use one of the IAppBuilder’s extension methods that allows you to add an owin middleware as a lambda expression.
Everything is the same as above, but instead of Global.asax add this as your first middleware in your owin startup class (usually Startup.cs )
public class Startup { public void Configuration(IAppBuilder app) { RouteConfig.RegisterRoutes(RouteTable.Routes); app.Use((owinContext, next) => { if (ConfigurationManager.AppSettings["test-security"] != "true") return next.Invoke(); var username = ConfigurationManager.AppSettings["username"]; var roles = ConfigurationManager.AppSettings["roles"].Split(' '); ClaimsIdentity identity = new ClaimsIdentity(authenticationType: "test-security"); identity.AddClaim(new Claim(ClaimTypes.Name, username)); roles.ToList().ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role))); ClaimsPrincipal principal = new ClaimsPrincipal(identity); //HttpContext.Current.User = principal; //this will work as well if you are hosting in IIS, //but if you are using owin, might as well use the owin //to set the principal owinContext.Authentication.User = principal; return next.Invoke(); }); //rest of your owin startup configuration } }
You can find this example on github here.