JWT Token Based Authentication in .NET Core

What is JWT(JSON Web Token)?






JSON Web Token(JWT) is a way of securely transmitting information between client and server as a JSON object.

This information is digitally signed using a secret key (HMAC algorithm)
Basically it is used for Authorization and Information Exchange.

Structure of JWT:

  • Header (Algorithm & Token Type)
  • Payload (Data)
  • Signature (Header + Payload + Key) > Signature is the token which ever is generating by JWT.


What is Principal:

A Principal in a computer security is an entity that can be authenticated by computer system or network.

It has three components which are:

  1. Identity: Identity contains the information which is used to identify a user.
  2. Roles: A user may have different access rights.
  3. Claims: A user may have multiple identities (like in Real Life Passport, Driving License, etc.), In our scenario identities will be called as Claims.


Advantages of JWT:

  • JWT is stateless - How it is stateless: because as we are generating a new token by authenticating a user no need to store token anywhere.
  • JSON Parsers are common in most programming languages.
  • Client-side support on multiple platforms including mobiles.




Code Approach:

Nugets: 

  1. Microsoft.AspNetCore.Authentication.JwtBearer

Step 1: Creating a API call for validating a user. (Login/Auth Controller).

Step 2: Generating a token upon successful login validation.

Further steps followed after example code.

For the above two steps below is the code.
First method is to create API call for validating a user, and second method is used to generate a token upon validation.

LoginController.cs

using CoreAPI.Contexts;
using CoreAPI.Models;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims;
using System.Text;

namespace CoreAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class LoginController : Controller
    {
        IConfiguration _Configuration;
        public LoginController(IConfiguration configuration)
        {
            _Configuration = configuration;
        }

        [HttpPost]
        public IActionResult Post(User user)
        {
            using SampleDBContext context = new SampleDBContext(_Configuration);

            if (context != null && context.user != null)
            {
                var validateUser = context.user.Where(v => v.Email == user.Email
                                    && v.Password == user.Password).FirstOrDefault();
                if (validateUser != null)
                {
                    User userObject = validateUser;
                    userObject.UserMessage = "Login Success";
                    userObject.AccessToken = GetToken(userObject);
                    return Ok(userObject);
                }
            }
            else
            {
                return NotFound("User Not Found!");
            }

            return BadRequest();
        }

        // Generating Token
        private string GetToken(User user)
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub,
                            _Configuration["Jwt:Subject"]??""),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()),
                new Claim("UserId",user.Id.ToString()),
                new Claim("DisplayName",user.Name??""),
                new Claim("UserName", user.Name??""),
                new Claim("Email",user.Email??"")
            };

            var key = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_Configuration["Jwt:Key"] ?? ""));
            var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                    _Configuration["Jwt:Issuer"],
                    _Configuration["Jwt:Audience"],
                    claims,
                    expires: DateTime.Now.AddMinutes(5), // Expire Time
                    signingCredentials: signIn
                );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }
}

User.cs:

namespace CoreAPI.Models
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
        public string? Password { get; set; }
        public string? UserMessage { get; set; }
        public string? AccessToken { get; set; }
        public DateTime CreateDate { get; set; }
        public bool IsActive { get; set; }
        public string? Role { get; set; }
    }
}


Step 3:

In appsettings.json add "Jwt" section.

Key: Any random key can be used.

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "SampleDb": "Data Source=.\\SQLEXPRESS;Initial Catalog=Sample;
            Integrated Security=True;TrustServerCertificate=True;"
  },
  "AllowedHosts": "*",
  "Jwt": {
    "Key": "wTEM5879rtYud789452Kiua",
    "Issuer": "JWTAuthenticationServer",
    "Audience": "JWTAuthenticationClient",
    "Subject": "JWTServiceAccessToken"
  }
}


Step 4:

In startup.cs file add services.AddAuthentication part as mentioned below.

Startup.cs

using CoreAPI.Authentication;
using CoreAPI.Contexts;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace CoreAPI
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();  
            services.AddEndpointsApiExplorer();
            services.AddSwaggerGen();

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

            services.AddAuthorization();

           
        }

        public void Configure(IApplicationBuilder app, IHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseHttpsRedirection();           

            // Keep in mind below order need to be maintained
            // for below three middlewares
            app.UseAuthentication();
            app.UseRouting();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Step 5:

Finally decorate the "[Authorize]" atrribute in all controllers.

using CoreAPI.Contexts;
using CoreAPI.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace CoreAPI.Controllers
{
    [Authorize] // Authorize to validate the token
    [ApiController]
    [Route("[controller]")]
    public class HomeController : Controller
    {
        public IConfiguration configuration { get; set; }
        public HomeController(IConfiguration _configuration)
        {
            configuration = _configuration;
        }

        [HttpGet(Name = "GetAllEmployees")]
        [Route("~/Home/GetAllEmployees")]
        public IEnumerable<Employees> Get()
        {
            using var context = new SampleDBContext(configuration);
            List<Employees>? employees = new List<Employees>();
            if (context != null && context.employees != null)
            {
                employees = context.employees.ToList();
            }

            return employees;
        }
    }
}


Testing: 

Get the token by posting valid user details to Login.

Copy the access token from output (Selected in below image).



In postman go to Authorization select bearer token pass the token in textbox and send.



How to use bearer token in C# HttpClient:

public async Task<string> HttpCall(string accessToken)
{
    using (var client = new HttpClient())
    {
        var url = "<ApiUrl>";
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
        var response = await client.GetStringAsync(url);

        return response;
    }
}


2 Comments

  1. How did you generate "Key": "wTEM5879rtYud789452Kiua", and followed Issuer, Audience and subject. Please describe the steps. Also, how to implement it for role based authentication.

    ReplyDelete
    Replies
    1. Hi Priyatham, You can use any random key it is a user defined key. Mentioned details @ step 3.

      Delete

Contact Form