Implement JWT Authentication in ASP.NET Core APIs

If you developed web applications using ASP.NET before, chances are you are already familiar with cookie authentication. Although cookie based authentication is still available under ASP.NET Core, JSON Web Token or JWT based authentication is becoming more and more common. To that end this article aims at introducing you to JWT and JWT based authentication.

What is JWT?

Before we delve into the code level implementation details let's briefly understand a few concepts related to JWT.

As mentioned earlier JWT stands for JSON Web Tokens. It's an open standard to pass user data between client and server. JWT has many advantages over traditional cookie authentication. JWT is more secure and can also be used with non-browser clients. JWT is a prefferred choice for implementing authentication in Single Page Applications (SPA). A JWT token consists of three parts namely header, payload, and signature. You will learn about them in the later part of this article.

Unlike cookies, which are passed automatically to the server, JWT needs to be explicitly passed to the server. So, a simplified flow of operations would be:

  • Client sends security credentials such as user name and password to the server for validation.
  • Server validates the user name and password.
  • If found correct the server generates and issues a JWT token to the client.
  • The client receives the token and stores it somewhere.
  • While requesting any resource or action from the server, the client adds the JWT token issued earlier in the Authorization header.
  • Server reads the authorization header to retrieve the JWT token.
  • If the token is valid, the server performs the action requested by the client

So, simply put JWT acts like a ticket. If the incoming request has a ticket, it is allowed to access a resource.

Steps required to implement JWT based authentication

In order to implement JWT based authentication you need to perform the following steps:

  • Store JWT details in a configuration file.
  • Enable JWT authentication scheme in the application startup.
  • Create some mechanism that validates user name and password, and issues a JWT.
  • Create a secured API
  • Invoke the API from a client

Let's perform these steps one-by-one.

Begin by creating a new ASP.NET Core API project.

Notice that I am using Visual Studio 2019 and ASP.NET Core 3.0 but you can use Visual Studio 2017 and ASP.NET Core 2.2 also.

Store JWT details in a configuration file

Once the project is created, add appSettings.json file to it and add the Jwt section as shown below:

"Jwt": {
  "Key": "SomeReallySecretKey",
  "Issuer": "SomeIssuer",
  "Audience": "SomeAudience"
}

Of course, the section name need not be Jwt. You can give any name of your choice. The Jwt section defines three keys - Key, Issuer, and Audience.

The Key is supposed to be a secret string that is used while signing the token. Issuer indicates the party that is issuing the JWT and audience indicates the intended recipients of the JWT. 

Enable JWT authentication scheme in the application startup

Now open the Startup class and modify the ConfigureServices() method as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion
(CompatibilityVersion.Version_2_2);

    services.AddAuthentication
(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
  options.TokenValidationParameters = new TokenValidationParameters
  {
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = Configuration["Jwt:Issuer"],
    ValidAudience = Configuration["Jwt:Audience"],
    IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
  };
});
}

We call AddAuthentication() and specify JWT bearer scheme to be the authentication scheme. We also specify various options for the JWT bearer scheme. If you carefully observe the TokenValidationParameters object you will find that it indicates whether issuer, audience, lifetime, and signature key is to be validated or not. Additionally we also specify a valid issuer, a valid audience, and a valid signing key. These values are retrieved from the configuration file.

Now go to Configure() method and add the following code:

public void Configure(IApplicationBuilder app,
 IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseStaticFiles();

    app.UseAuthentication();

    app.UseMvc();

}

We call UseAuthentication() to wire the authentication middleware into the HTTP pipeline.

Create an API that validates a user and issues a JWT

We need some mechanism that validates a user name and password. We will go with an API that does that for us. So, add a new API controller called SecurityController in the Controllers folder. The SecurityController will have two private helper methods and a public action.

The first private helper method is called GenerateJWT() and it generates a JWT token for us. The token is then sent to the client. The GenerateJWT() method is shown below:

private string GenerateJWT()
{
    var issuer = _config["Jwt:Issuer"];
    var audience = _config["Jwt:Audience"];
    var expiry = DateTime.Now.AddMinutes(120);
    var securityKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var credentials = new SigningCredentials
(securityKey, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(issuer: issuer, 
audience:audience,
expires: DateTime.Now.AddMinutes(120), 
signingCredentials: credentials);

    var tokenHandler = new JwtSecurityTokenHandler();
    var stringToken = tokenHandler.WriteToken(token);
    return stringToken;
}

The above code retrieves the issuer, audience, and key from the configuration file. It then creates a new SymmetricSecurityKey based on the Key. SigningCredentials object is then generated based on the SymmetricSecurityKey. Notice that we use HS256 algorithm while generating the digital signature.

Now we can move ahead and create a JWT token. This is done using JwtSecurityToken class. We pass the issuer, audience, an expiry DateTime for the token, and the signing credentials in the constructor.

We want the JWT in a string form so that it can be easily sent to the client. This is done using JwtSecurityTokenHandler class. The WriteToken() method accepts a JwtSecurityToken created earlier and returns it as a JSON compact serialized format string.

The second private helper method is ValidateUser() and is shown below:

private bool ValidateUser(User loginDetails)
{
    if (loginDetails.UserName == "User1" && 
loginDetails.Password=="pass$word")
    {
        return true;
    }
    else
    {
        return false;
    }
}

The ValidateUser() accepts User object:

public class User
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

Inside, it checks the user name and password. In the above example, we simply check against hard-coded values. But you could use ASP.NET Core Identity or any custom technique to validate a user. If the user credentials are valid we return true, otherwise we return false. Instead of returning true you could have also returned user details such as user name and roles. Here, for the sake of simplicity we don't return any such details.

Finally, we need Login() action that will invoke ValidateUser() and GenerateJWT() helper methods.

[HttpPost]
public IActionResult Login([FromBody]User loginDetails)
{
    bool result = ValidateUser(loginDetails);
    if (result)
    {
        var tokenString = GenerateJWT();
        return Ok(new { token = tokenString });
    }
    else
    {
        return Unauthorized();
    }
}

Note that Login() action of SecurityController is marked with [HttpPost] attribute. It accepts User object as the parameter. The Login() action will be invoked by the client application and the client, by some means, will supply the User details such as user name and password.

Inside, we call ValidateUser() helper method to check whether the user name and password are valid. If user credentials are valid, we call GenerateJWT() to generate a JWT token. The string token is returned to the client with HTTP status of Ok (status code - 200).

Before we go ahead let's quickly test our Login() action.

Run the API project either through Visual Studio or using .NET CLI. Start Postman and enter URL as http://localhost:5000/api/security.

Make sure to select the HTTP verb to be POST. And also specify the JSON body that wraps UserName and Password as shown above.

Now click on Send button. If all goes well, you will see a response as shown below:

This is the JWT token! Notice that the token takes the form of -- header.payload.signature.

Ok. Now let's see what's the actual content of this token.

Open your favorite browser and navigate to jwt.io. Locate Encoded section under Debugger and copy-paste just the token value in the encoded textbox. Also locate Verify Signature section and copy-paste your Key from the appsettings file. You should see the decoded form of the token as shown below:

 

Have a look at the header and payload. 

Create a secured API

Ok. So far so good. Now let's create an API that requires security. Add another API in the Controllers folder - EmployeeController. For the sake of simplicity we will have only GET method in it.

[Authorize]
[HttpGet]
public IActionResult Get()
{
    List<Employee> data = new List<Employee>();
    data.Add(new Employee() {EmployeeID=1,
FirstName="Nancy", LastName="Davolio" });
    data.Add(new Employee() { EmployeeID = 2, 
FirstName = "Andrew", LastName = "Smith" });
    data.Add(new Employee() { EmployeeID = 3, 
FirstName = "Janet", LastName = "Rollings" });
    return new ObjectResult(data);
}

The Get() action simply creates a few Employee objects and returns them as an ObjectResult. The Employee class looks like this:

public class Employee
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

What's important is that this method is marked with [Authorize] attribute. The [Authorize] attribute indicates that only authenticated users can invoke Get() action.

Let's confirm whether it works that way or now.

Open man tool again. This time make a GET request to http://localhost:5000/api/employee.

Recollect that under JWT based security, the client needs to explicitly send the token to the server through Authorization header. So, while making the GET request we need to add the Authorization header and set its value to the JWT token we generated earlier.

To add the authorization header, go to Headers tab and enter KEY as Authorization and VALUE as Beader <string_token_here>. Notice that it's Bearer, followed by a white space, followed by the actual token.

Now click on Send button. If all goes well, you should see a list of employees.

 

In this part we used Postman to invoke the API. In the next installment of this article we will build a simple JavaScript client to call the API.

That's it for now! Keep coding!!


Bipin Joshi is a software consultant, trainer, author, yoga mentor, and spiritual guide having 24+ years of experience in software development, consulting, and training. He conducts instructor-led online training courses in ASP.NET Core, ASP.NET MVC, and Design Patterns for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get article updates : Facebook  Twitter  LinkedIn

Posted On : 21 January 2019


Tags : ASP.NET ASP.NET Core MVC .NET Framework C# Visual Studio


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.

  

Receive Weekly Updates