The first thing would be to make a new
IdentityController which is going to log users in and issue tokens that we can use to authorise them.
The services that this controller needs is the
IIdentityService , which we are going to create and register. Go ahead and create a POST endpoint at
/api/v1/identity/register that receives a
UserRegistrationRequest contract — essentially having properties:
You can really add as many properties as you want, but this is kept as-is for demonstration purposes.
We can now create a domain object called
AuthenticationResult which is going to contain the
Token , a
Success boolean and a list of
ErrorMessage strings. That’s for our internal usage to figure out what happened with the request.
We can also go ahead and create 2 more contracts:
AuthSuccessResponse , which essentially returns the
Token back and a
AuthFailedResponse which is a list of errors as well. That is not necessarily the best practise, because you might want to check more user credentials or verify their emails in another manner, but this is just a tutorial.
Let’s create our
Task<AuthenticationResult> RegisterAsync(string email, string password) method in our
IdentityService . We can grab a .NET service called
UserManager<IdentityUser> (registered already from our
DbInstaller), which we are going to use.
- First, check if the user exists by
- If user exists, return a new authentication result (failed) stating that the user exists in the list of errors
- Else, create a
new IdentityUser, using the
- Finally, create the user by
await _userManager.CreateAsync(newUser, password). The reason we pass the password to this method and not to the IdentityUser one is because this way, the password is going to be salted and hashed via Microsoft’s password hasher, in an industry standard way
Just return the operation’s result by checking
TIP: There is
.Errors.Select(x => x.Description)
With our user registration done, we can issue the token.
Just grab a
new JwtSecurityTokenHandler , our key, by
We need to inject our claims right now. It’s quite a difficult concept to understand. Claims are essentially the information about the user we embed into the token. Stuff like user id, email, roles, permissions, genders, etc.
JwtRegisteredClaimNames for us to use. For now, we are going to just add
Jti (a unique id for this specific JWT — we will use that later for token invalidation) and a custom claim
Some extra metadata is the
Expires , typically a couple of hours.
Finally sign the token and return it as a
In our controller, if the
authResponse is not successful, return a
BadRequest() returning a
AuthFailedResponse containing the list of errors.
Let’s see in practise what this actually looks like
- By default, our password has some strength requirements (you can change the default password rules)
- If we change the password to something stronger, we get our JWT back
- Let’s see what this token has at jwt.ms
If at the Swagger UI, you click
Authorise and write
Bearer _paste token_ , you are authorised.
Let’s see right now how this works. If we want every endpoint of the controller to be authorised, we can just add the
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] on the controller class.
If we now try to use the
/posts API without the token in our header, we should get a
401 Unauthorised response
If we use our token as an authorisation header, we can normally use the API:
The user is also created. We are going to use this for logging in, in the next episode.
Up Next: User Login