.NET Core 3.0 (Preview 4) Web API Authentication from Scratch (Part 3): Token Authentication
JSON Web Tokens (JWT)
Now we have the Auth Repository available (follow part 1 and part 2 of this series) for us to use. Let’s go ahead and create AuthController
inside Controllers directory.
Note: This article is originally written for .NET Core 3.0 (Preview 4), so in some places you will see
—- version
flag when I install packages. Feel free to install the latest version by removing the—- version
flag, newer versions should work as expected. As an example you can usedotnet add package System.IdentityModel.Tokens.Jwt
instead ofdotnet add package System.IdentityModel.Tokens.Jwt — version 5.4.0
Note 2: I have updated the project to .NET 5.0 can be accessed from following URL. It’s not a frustrating process, as mentioned earlier make sure packages are at latest version.
We also need a route so the client applications will call in. <host url>/api/auth
will be our endpoint so we’ll add the route as api/[controller]
, also we need to inject AuthRepository
. Here’s how it looks like,
Let’s create Register
method. This is gonna be a [HttpPost]
because we need to get information from the users.
DTOs
We will be passing username and password as a JSON serialized object. To get username and password from that serialized object, we need to create an object that will resemble the incoming massage. DTOs (Data Transfer Objects) are a way of doing this. Take a look.
Create a new folder called Dtos
and inside create new class UserForRegisterDto
with fields, string username
and string password
Final Register
method looks like this. make sure to add these namespaces at the top.
using System.Threading.Tasks;
using JWTAuth.API.Models;
using JWTAuth.API.Dtos;
Final AuthController
with register method looks like this.
We are converting the username to lowercase because somebody may use “John” as username and somebody else will use “john” with lowercase ‘j’. So there could be two johns. We don’t want that scenario. So before storing it in the database we are converting everything to lowercase. When our API checks for existing usernames using UserExists()
later, it will work as expected.
Now let’s send a postman request and see if we did it correctly.
Note that the endpoint is https://localhost:5001/api/auth/register [1], and it is a POST
request [2]. You also need to add a header with Content-Type
with value as application/json
[3] make sure you have set the body content as Json [4] and type the request in this format [5].
{
"username": "John",
"password": "password123"
}
Which will result in 201 Created
status code [6] as we set in our AuthController
return StatusCode(201);
Take a look at the database to see if the user has been created.
Let’s add some additional validation for our API. Go to UserForRegisterDto
and change code as follows. Don’t forget to add namespace,
using System.ComponentModel.DataAnnotations;
This will make username and password as required fields and password rules. Let’s send empty object in postman to test it out.
{
"username": "",
"password": ""
}
See, we get error messages.
Token Authentication
Next we will look at Login
method. When a user logs in we need to send an authentication token to the user. We will be using JWT or JSON Web Tokens to do that. Learn more about JWT
Again let’s create a DTO for login as UserForLoginDto
in Dtos folder.
Now let’s create Login method. First we are getting the user from repository. If user login failed we will return 401 Unauthorized
. Next part will create a JWT token as an object to the caller of this login method.
First install System.IdentityModel.Tokens.Jwt from CLI
dotnet add package System.IdentityModel.Tokens.Jwt --version 5.4.0
Add following namespace to the top of AuthController
.
using Microsoft.Extensions.Configuration;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System;
Add a section to appsettings.json
with a secret key with this format. In my case, the key is “secret key for jwt”.
"AppSettings":{
"Token": "secret key for jwt"
}
Now for this to be used in AuthController
we need to inject it. Just like we did to AuthRepository
.
private readonly IAuthRepository _repo;
private readonly IConfiguration _config;public AuthController(IAuthRepository repo, IConfiguration config)
{
_repo = repo;
_config = config;
}
And the login method.
Full AuthController
.
Let’s send a postman request to https://localhost:5001/api/auth/login [1] to see if that works,
Note that it is a POST
request with headers application/json
having a JSON body,
{
"username": "John",
"password": "password123"
}
with the user that we registered earlier, which returns status code 200 Ok
with tokenString
. You can copy this token and go to https://jwt.io and decode it.
In payload you will see nameid
as 1 and unique_name
as john. This is because we told the token to return those information with the token when we add Claims.
Subject = new ClaimsIdentity(new Claim[]{
new Claim(ClaimTypes.NameIdentifier,userFromRepo.Id.ToString()),
new Claim(ClaimTypes.Name, userFromRepo.Username)
}),
Now we need to specify other methods which are not allowed to be accessed without the token. The ValuesController
we created in the first part of this article still can be accessed without logging in.
Let’s made it restricted, so without the token it cannot be accessed.
Authentication Middleware
First install this package using CLI,
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 2.2.0
Then go to Startup.cs
add these lines to ConfigureServices
method.
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
Full Startup.cs
looks like this. Full csproj
file looks like this.
Add these namespaces to the top,
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
In Configure
method, make sure you have app.UseAuthentication();
after app.UseRouting();
[Note — app.UseAuthentication();
and app.UseAuthorization();
are two different things.]
Add,
using Microsoft.AspNetCore.Authorization;
namespace to the top of ValuesCotroller
. Then add [Authorize]
attribute to the specific method or for entire controller. I’ll add it to the controller.
Now, if you send the GET
request to https://localhost:5001/api/values it will return 401 Unauthorized
that’s because you need to send the token with the request.
Send login request again and get a token. Go back to postman, add a new header Authorization
with value Bearer <token>
, note that there’s a space between bearer and token.
Done!. You have successfully implemented token authentication in .net core!
Visit here to get full code for this project. Hope you learnt something from this 3 articles. Happy coding!! :)