(C# ASP.NET Core) JWT Authentication in WebApi

JWT stands for JSON Web Token. In its simplest form, JWT authentication is a two step process. First a client obtains a JWT token from the server. This token is signed with a secure key and encrypted with a standard encryption algorithm. It has an expiry date, and it contains a list of claims such as the user-id, user-role, his email, etc., The client obtains authorization to a web api by sending this token in an authorization header. In this tutorial we shall see a server side implementation of the code for generating a JWT token, and of protecting a web api.
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

Getting the Project Ready

The source code of the project has been attached as a download with this tutorial. Open the project in visual studio and install the nuget package for JWT authentication. Ensure that the installation succeeds.


Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

Secondly, this project is built on the project that we have discussed in the previous tutorial where we implemented basic authentication by inheriting a class from AuthenticationHandler. You might want to go through that tutorial for a recap.

Video Explanation (see it happen!)

Please watch the following youtube video:

Source Code

Locate the Program.cs file and double click to open it!


// program.cs 
// MUST SEE THE SOURCE CODE IN YOUR DOWNLOADS 
// BasicAuthentication class skipped here 

// Install-Package Microsoft.AspNetCore.Authentication.JwtBearer  

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using MyRazorApp.Utility;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

var builder = WebApplication.CreateBuilder();

builder.Services.AddRazorPages();

// hard-coded only for tutorial 
// NOT RECOMMENDED 
// store in settings file  
// key must be greater than 32 bits/16 unicode chars 
const String CLIENT_SECRET = "ab_some_key_123456";

var jwtParams = new TokenValidationParameters
{
    IssuerSigningKey =
        new SymmetricSecurityKey(Encoding.UTF8.GetBytes(CLIENT_SECRET)),

    ValidIssuer = "https://your_domain.com",

    ValidAudience = "https://your_domain.com/your-api-endpoint"

};

const String BASIC_SCHEME_NAME = "Basic";

builder
    .Services.AddAuthentication()
    .AddScheme<AuthenticationSchemeOptions, 
            BasicAuthHandler>(BASIC_SCHEME_NAME, null)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = jwtParams;
    });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthentication();

app.UseAuthorization();

app.MapRazorPages();

// PLEASE SEE NEXT TUTORIAL WHERE THESE 
// API ARE CALLED FROM A C# Console APP 

// step 1. user gets token by calling this api 
app.MapGet("/token", 
    [Authorize(AuthenticationSchemes = BASIC_SCHEME_NAME)]
(ClaimsPrincipal principal) =>
{
    var jwtToken = 
            new JwtSecurityToken(
                    jwtParams.ValidIssuer,
                    jwtParams.ValidAudience,
                    principal.Claims,
                    null,
                    DateTime.Now.AddMinutes(200),
                    new SigningCredentials(
                        jwtParams.IssuerSigningKey, 
                        SecurityAlgorithms.HmacSha256
                        )
                );

    // get the token 
    String strTok = new JwtSecurityTokenHandler().WriteToken(jwtToken);

    return Results.Ok(new { access_token = strTok });
});

// PLEASE SEE NEXT TUTORIAL WHERE THESE 
// API ARE CALLED FROM A C# Console APP 

// Step 2. user sends the token in a bearer header 
app.MapGet("/info",
    [Authorize(
        AuthenticationSchemes = 
            JwtBearerDefaults.AuthenticationScheme)] () =>
{
    return Results.Ok("jwt auth succeeded!");
});

app.Run();

First we have the namespaces. You can always come back to examine these if you get compilation errors.

Next we have a string for client secret. This key should NOT be hard-coded as we have done here. In a real project it should be saved in a more secure place such as app settings or environment variables. The length of the key must be greater than 32 bits or 16 unicode characters.

After that TokenValidationParameters are created. Client Secret is used to construct a signing key, the issuer is set to your api website, and audience is set to your web api end-point. These are not hard and fast rules, but I suggest that you may please refer the JWT website https://jwt.io/ for finer details because that would be beyond the scope of this tutorial.

See the linked video for a clearer explanation.

The next step is to register the services for Basic Authentication and JWT Bearer Authentication. TokenValidationParameters are now attached as JwtBearerOptions.

The next five lines are the usual boilerplate code that we already know.

Next we have a GET API that is called by the user to obtain his JWT Token. This webapi is authorized using basic authentication - which means that the user has to send his login and password through an authorization header. Once the user authenticates, a JwtSecurityToken is created on the basis of TokenValidationParameters.

After that this security token is used to obtain the string for JWT Token, and it is sent as a json property of name access_token. It could have been of any name, but it seems that this is the name that has now become almost a convention.

The user has been provided a JWT Token through a call to a web api that itself is protected by basic authentication. This is not the only way. Some websites have a dedicated page for showing the JWT Token. For this they have a login form where the user enters his credentials and after a successful authentication, the same page presents a JWT Token + its expiry date that the user can copy and use it for subsequent calls. I have seen this technique till recently on the zoom meeting website.

Lastly, we have a web api that is protected through JWT Authentication. The handler need be only marked with an [Authorize] attribute, and everything else is taken care of by the nuget package that we added at the start of this tutorial.


This Blog Post/Article "(C# ASP.NET Core) JWT Authentication in WebApi" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.