No! You don't need to use ASP.NET Identity!

Going way back to, I think, .NET v3, ASP.NET had this new thing called Membership. Maybe it was a version earlier. I dunno. "Neat," I thought, I can write a provider adhering to this interface and use my existing user and auth structure to plug into this system. Then I saw that the membership and role providers each had about a bazillion (maybe quadbazillion) members to implement, and reality set in that what I already had was working just fine. Some years later, ASP.NET offered Identity, this newer thing that did sort of the same thing. It even made its way into Core.

You don't need it. For real. I'm not saying that it isn't a useful piece of the framework, but you need to stop making it the default for user management. It's not hard or time consuming to build out your own system of user entities and permissions (roles, claims, etc.) as you see fit. The problem, as I see it, is that developers are confusing the act of persisting user information with authentication. I get why that may be, as Identity uses one line of code to both verify a user and sign them in (Core docs show how). But under the covers, there is code that first verifies the user/password against the database, then sets the auth cookie to indicate who the user is for future requests. You can in fact do one without the other.

Why would you do that? Part of it may just be an issue of control, but for me, it's because I want to be very specific about how I structure my user data. I also don't really want to use Entity Framework in many cases (read: most things I port from older apps), and EF is part of the magic of Identity. What I've seen in a number of projects is the use of Identity mixed with a home-grown set of user domain objects and a totally separate database or persistence mechanism. If you're doing all of that plumbing anyway, you definitely don't need the additional overhead of Identity.

Let's use ASP.NET Core as an example, first. In Startup, we use the Configure method to use cookie-based authentication:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
   AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
   AutomaticAuthenticate = true
});

In some kind of login method, from our MVC controller, we look up the user in the code that we wrote, with whatever backing store we made, and then sign in. Let's pretend that myUser is some construct we've made up:

var myUser = _myUserLookerUpperService(email, password);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, myUser.Name)
}; var props = new AuthenticationProperties
{
IsPersistent = persistCookie,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), props);

The code should be pretty straightforward. Whatever our domain-specific user thingy is, it's something built for us, instead of the generic thing that the Identity framework has created. We use that to construct a set of claims and authentication properties, and then use the built-in Authentication system to sign in with our newly constructed principal. This is what creates the encrypted cookie on the user's browser. It's not as magic as the Identity service, but remember that you're welcome to use any kind of schema that you want to persist user data, and that means you can query it or normalize it (if you must) against any other bits of data you have.

Naturally, you may want to set up some other context, or simply verify that they're still a known-good user on each request. To do that, you can wireup middleware in the Startup's Config method (app.UseMiddleware<MyMiddleware>();). Middleware doesn't use an interface (and I don't know why they chose convention over an interface), but it does expect an Invoke method to do stuff. It's here that you would look up the user based on the identity:

public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var name = context.User.Identity.Name;
if (!string.IsNullOrWhiteSpace(name))
{
var userService = context.RequestServices.GetService<IMyUserLookerUpperService>();
var user = userService.GetUserByName(name);
if (user != null)
{
// do stuff here
}
else
{
// do something about your bad user
}
}
await _next(context);
}
}

Again, I believe that the Identity framework has some plumbing for this, but if you're a control freak like me, this is better. The official documentation has a really great write up on using this cookie mechanism without Identity.

If you're still using ASP.NET 4.5 and MVC on top of it (or even WebForms), you don't need to use Identity here either. In your MVC action, or your event handler in WebForms, you can use Forms Authentication to do the same work, without any setup (though you can change the cookie name and some other things via web.config):

var user = _myUserLookerUpperService(email, password);
var ticket = new FormsAuthenticationTicket(1, user.Name, DateTime.Now, DateTime.Now.AddDays(30), createPersistentCookie, "");
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.Expires = DateTime.Now.AddDays(30);
context.Response.Cookies.Add(cookie);

Neat, right? The static FormsAuthentication class also has a SignOut method. Instead of middleware, we can use an IHttpModule or an IActionFilter to act on user data as appropriate.

To circle back, the point here is that Identity is great to spin up some user account persistence and authentication quickly, but if you want to do your own thing, or don't want EF involved, or you're a control freak, understand that you don't need Identity to auth your users.

EDIT 9/6/16: Andrew Lock has a pretty solid outline of how claims-based identities should be used. He goes way more in depth about creating a new principal and signing in the user, but note that he's not getting into the business of persistence here.

7 Comments

  • Awesome!! This is exactly what I was looking for!! All these identity frameworks have been pathetic.
    The above summarizes the most minimal things to do for authentication. And then we can add a few things for anti forgery in the individual pages.

  • Thanks for the info!

  • Should we really use middleware for user authorization instead of filters which run on MVC requests so called less?

  • Depends entirely on your needs. Action filters are harder to test, and especially in Core, MVC *is* middleware. I'm sure some people have strong opinions about where and when to architecturally mix routing, content generation and auth concerns.

  • I don't use cookies at all, as I don't use MVC and wato to have app compatible with mobile API. So how can I do it without framework ?

  • Hello
    Same thoughts.... but you need it when you don't want or have time to build your own :) I'm not a big fan of EF either, not in any use case, so I switched it out with MartenDb and Postgresql. Happy camper!!

  • Decent article, yessir. That's been my criticism for a long time re: IF, that, out of the box, one is "locked" into adopting EF in some way shape or form. But it needn't be the case, one can in fact roll their own implementation, and connect their own bits as they see fit. Let alone anything more taylored as you're doing here. Great instruction here, hope to learn a thing or three about it more approaching my own arch'es.

Add a Comment

As it will appear on the website

Not displayed

Your website