Winner's Excogitations

A chronicle of the thoughts, learning experiences, ideas and actions of a tech junkie, .NET, JS and Mobile dev, aspiring entrepreneur, devout Christian and travel enthusiast.

Json Web Token (JWT) Authentication with ASP.NET Core 2.0

6 years ago · 4 minutes read

I recently was building a small web application and needed to add some authentication. For the .NET developer, the first thing that comes to mind is Identity but was that too heavy for my purposes. Having worked with Json Web Tokens (JWT) with NodeJS, they seemed to be the perfect fit for the job. They are simple, easy to implement, and for the most part, do a good job of securing your endpoint(s).

The wrong way

That said, I initially approached implementing token-based authentication the same way i had done it on Node which was a pretty terrible idea. I created middleware that sat on the HTTP request pipeline and for every request, I checked the route to see if it was to be protected and then proceeded to look for the token header, retrieve it, decode it and respond depending on the state of the token. That led to this abomination

    public class AuthenticationMiddleware
    {
        private readonly RequestDelegate _next;
    
        public AuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.Value.Contains("notes"))
            {
                string token = context.Request.Headers["x-access-token"];
                if (token == null)
                {
                    context.Response.StatusCode = 401;
                    await context.Response.WriteAsync("Sorry, a token is required to access this route.");
                    return;
                }
    
                try
                {
                    var json = Helpers.DecodeToken(token);
                    context.Items["id"] = json["id"];
                    await _next.Invoke(context);
                    return;
                }
                catch (TokenExpiredException)
                {
                    context.Response.StatusCode = 401;
                    await context.Response.WriteAsync("Sorry, your token is expired. Please login.");
                    return;
                }
                catch (SignatureVerificationException)
                {
                    context.Response.StatusCode = 401;
                    await context.Response.WriteAsync("Sorry, this token has an invalid signature.");
                    return;
                }
                catch (ArgumentException)
                {
                    context.Response.StatusCode = 401;
                    await context.Response.WriteAsync("Sorry, this token is corrupted.");
                    return;
                }
            }
    
            await _next.Invoke(context);
        }
    }
    
    public static class AuthenticationMiddlewareExtensions
    {
        public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder app)
        {
            return app.UseMiddlewarelt;AuthenticationMiddlewaregt;();
        }
    }

Somewhere at the back of my mind, I knew this couldn’t be the right way. So off I went to do some research and it turns out I was right, there is a better way.

The right way

This tutorial makes a whole lot of assumptions. First, you know what Json Web Tokens are. If you don’t, then this scotch.io article would do you a whole world of good. Second, you are familiar with the C# language, .NET Core and the ASP.NET Core framework. if you are not familiar with them, this, this and this would help you get up to speed. Third, you have an ASP.NET Core application. Finally, you have a system of verifying if a user is valid or not.

That said, we need to create a static method called GenerateAuthToken that would help us generate the authentication token (static so as to enable it be called from any class without instantiation). You can place the method in any existing class or create a class for that purpose. The method should look something like this. This sample takes a userId as the token payload.

    internal static string GenerateAuthToken(string userId)
    {
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, userId),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };
    
        var token = new JwtSecurityToken
        (
            "the issuer (change please)",
            "your audience (change please)",
            claims,
            expires: DateTime.UtcNow.AddDays(30),
            notBefore: DateTime.UtcNow,
            signingCredentials: new SigningCredentials(
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes("The security key (change please)")),
                SecurityAlgorithms.HmacSha256)
        );
        return new JwtSecurityTokenHandler().WriteToken(token);
    }

Once you are done verifying a user’s validity with a check against a database or by any other means, proceed to call the GenerateAuthToken method with the userId as a parameter. Something like this:

var token = GenerateAuthToken("sampleId");

A token would be retruned that looks something like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0.yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw

Next, to verify the user token when a request is made to the API, we need to configure the request pipeline. Add this to the <span class="pl-en">ConfigureServices</span> method in the ​Startup.cs file of your project to handle verifying the token:

     // add authentication
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddJwtBearer(jwtBearerOptions =&amp;gt;
     {
       jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
       {
         ValidateActor = true,
         ValidateAudience = true,
         ValidateLifetime = true,
         ValidateIssuerSigningKey = true,
         ValidIssuer = "the issuer (please change)",
         ValidAudience = "your audience (please change)",
         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("a secret (please change)"))
       };
     });

Add the code below to the Configure method of your Startup.cs file. It adds the authentication middleware to the request pipeline.

app.UseAuthentication();

Finally, to protect any controller or controller method, just add the Authorize attribute to the controller or controller method. That protects it from unauthorized access. if no token is present on the request or the token is invalid, a 401 response is returned.

PS: The JWT Bearer system expects the header to be in the format,

Authorization: Bearer .

Hopefully, this helps someone on their way to building the next big thing.

Till the next one.

Cheers.

 

Share on:
The Greatest Apostle?
An article putting forth my objections to our ranking of spiritual things because their value isn't determined by human.
Winner-Timothy Bolorunduro
Winner-Timothy Bolorunduro is a senior .NET developer with over 6 years experience helping organizations and individuals build compelling, stable and scalable web applications. Having spent the last three years in a fast-paced startup environment working remotely, he understands what goes into being part of a team that produces value for clients.

Comments