This repo is an example of how to set up a basic API with authorization and authentication using identities.
One of the key mistakes to make early on is to not inheret your application's DbContext
from IdentityDbContext
. It is a difficult change to make after your application is already established and when you inevitably need to implement identities, you are forced to either make a difficult database migration or, the more likely route, you simply set up a second DbContext
specifically for identity. Avoiding this complication is as simple as inhereting from the IdentityDbContext
from the beginning. There's no reason you cannot extend the user or role models either; you are free to add to them for things like user preferences.
Another point of contention you may see around the web is the issue of trying to force webapi to use JWT tokens exclusively. It is surprisingly difficult to convince the server to not automatically attach cookies on the responses. You may find some examples here and there and everywhere that all mention one of three things: either use .AddIdentity()
and just ignore the cookie, use .AddIdentity()
and then subsequently delete the cookie on every request 😒, or use .AddIdentityCore()
. If you are like me, then accepting the existence of the entirely useless cookie is not an option. So then what's the problem with AddIdentityCore()
? The problem with it is that the SignInManager is not properly added to the DI container by default and therefore is not available. Not only this, but the standard PasswordSignInAsync()
method actually relies on the cookie in the requests. So then what's the solution?
Check out the source code for AddIdentity()
, AddIdentityCore()
, and the relatively newer methods called AddRoles()
and AddSignInManager()
. If you compare, you will find that with AddIdentityCore()
, AddRoles()
and AddSignInManager()
, there is complete parity, it is just not documented anywhere.
So, the solution is to use these three methods to set up all necessary dependencies for handling authentication. As a bonus FYI, if you want to send and confirm verification emails, you will also need to load in the .AddDefaultTokenProviders()
.
builder.Services
.AddIdentityCore<AppUser>()
.AddRoles<AppRole>()
.AddSignInManager<SignInManager<AppUser>>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<AppDbContext>();
Oh, but there's one more catch! The PasswordSignInAsync()
depends on the cookie, remember? The solution is to simply use CheckPasswordSignInAsync()
instead. It does depend on fetching the user object before calling, but works all the same.
Now, finally, we have true JWT only authentication in our API.
Todo