Jwt Authentication using .Net Core 5

In this blog post we will look into JWT Authentication and its implementation using .Net Core Web Api. We will try to keep things as simple as possible, while taking time to explain the key concepts in best possible way. For same reason, we will mock our User repository with static entries.

Json Web Token

Json Web Token or JWT is an open standard for securely transmitting self contained messages between parties as Json Objects. These messages are verifiable as they are digitally signed. They are extremely compact compared to Simple Web Tokens (SWT) and could be used for Authentication as well as information exchange.

  • Authentication – After the initial sign in, each request send by client would contain JWT token, which would be read and used by the Server for authorization.
  • Information Exchange – Multiple parties could exchange secure information as they digitally signed. The signature component of the JWT(we will get it shortly) contains the hash of header and payload ensuring the message is not tampered with.

Structure of JWT

The key structure of JWT contains three parts in the following format.

Header.Payload.Signature

Let us examine each part.

Header – The Header contains information on the type of token and the hashing algorithm used. Typical header might look like

{
    'alg':'H256',
    'typ':'Jwt'
}

Payload – Payload contains claims, which are information about the entity and any additional information required. Claims are broadly categorized into 3 types.

  • Registered Claims – A set of predefined non-mandatory claims which can used to provide useful information.
  • Public Claims – Custom claims which are defined and registered with IANA (Internet Assigned Numbers Authoriry) Json web Token registry.
  • Private Claims – Custom claims which are defined and used by parties, but are neither predefined or registered.

Typical Payload might look like

{
    "iss":"www.hello.com",
    "sub":"123456",
    "name":"John Doe",
    "admin":true
}

Signature – The third and final part of JWT token structure is known as Signature. This comprises of Base64 encoded Header and Payload, which is then applied with the algorithm specified in the header with a secret key. The signed part ensure that the message wasn’t tampered with. Typical signature might look like

HMACSHA256( base64(header) + "." +   base64(payload), secret)

How does JWT Token work.

JWT authentication works in a simple way.

  • During the authentication process, the user inputs (username/password) are validated by the server and a token is generated. This token is then send back to the caller.
  • Caller stores the token internally and for each susequent request for a protected resource, it would include the token in the Authorization Header using the Bearer Schema.
  • The server would validate the further requests based on the token recieved. It would allow the request to access the protected source if the token is successfully validate.

Implementation of JWT Token in .Net Core 5 Web Api.

Alright, enough of talkng about JWT. Let us now start writing some code.

The first step would be to store our secret key and algorithm for signature in the AppSettings.Json, along with any additional information required. This might typically look like

"Jwt": {
    "Key": "ThisWouldBeReplacedBySecretKey",
    "Issuer": "www.bytelanguage.net",
    "Audience": "http://www.hello.com"
  }

We will now proceed to configure the authentication service and schema. The authentication is done using the IAuthenticationService which provides the ClaimPrincipal instance for the authorization to take decisions against. Schema refers to the authentication handlers and options for configuring each handler.

The Authentication Handlers create the Authentication ticket which represents the User Identity. Additionally it returns no result/failure when the Authentication fails. It also executes the challenge or forbid actions when an protected resource is accessed.

We configure the service and schema using the ConfigureService method.

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

In the above code, we have added registered the service required for authentication along with the Schema. AddAuthentication method registers the service used for authentication and returns a AuthenticationBuilder instance which can be further used for configuring the authentication. We have also specified that the JwtBearer would be responsible for authenticating the credentials using the token extracted from the Authorization Header, using the AddJwtBearer extension method.

.Net Core uses middlewares for handling Authentication and authorizations. The next step involves ensuring the Authentication and Authorization middlewares are plugged in our execution pipeline. This is done using the Configure() method.

app.UseAuthentication();
app.UseAuthorization();

A word of caution here. Since the middlewares are typically executed in the order they are registered, you need to ensure that the Authentication/Authorization middleware are in their right place. Usually you would want them after the routing middleware, and before the end points middleware.

Now that we have defined and registered our middlewares, it is time to define couple of services.

  • TokenService : which would be responsible for generating the token.
  • UserRepositoryService : Which would mock the user repository for us (Remember, as mentioned in the earlier part of article, we are not using any actual db in this example)

TokenService

The Token Service comprises of a single method.

public interface ITokenService
{
    string BuildToken(string key, string issuer, UserDto user);
}

The method would accept a secret key, an issuer and the UserDto, and would return a string representing the Jwt Token.

Let us go ahead and implement the service now.

public class TokenService : ITokenService
{
    private TimeSpan ExpiryDuration = new TimeSpan(0, 30,0);
    public string BuildToken(string key, string issuer, UserDto user)
    {
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.Role, user.Role),
            new Claim(ClaimTypes.NameIdentifier,
            Guid.NewGuid().ToString())
            };

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
        var tokenDescriptor = new JwtSecurityToken(issuer, issuer, claims,
            expires: DateTime.Now.Add(ExpiryDuration), signingCredentials: credentials);
        return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
    }
}

In the above code, we have created a set of Claims representing the User identity. We have also set a expiry for the token and then used the HmacSha256 algorithm to sign the token.

Repository Service

As mentioned earlier, this would be a simple class which would mock the User repository for us (as we do not want to complicate the example in hand with use of Db). For same reason, we will keep it as simple as possible.

public interface IUserRepositoryService
{
    UserDto GetUser(UserModel userModel);
}

public class UserRepositoryService:IUserRepositoryService
{
    private List<UserDto> _userCollection = new List<UserDto>();
    public UserRepositoryService()
    {
        _userCollection.AddRange(new[]
        {
            new UserDto("Anu Viswan","anu","admin"),
            new UserDto("Jia Anu","jia","admin"),
            new UserDto("Naina Anu","naina","admin"),
            new UserDto("Sreena Anu","sree","admin"),
        });
    }

    public UserDto GetUser(UserModel userModel)
    {
        return _userCollection.Single(x=>string.Equals(x.UserName,userModel.UserName) && string.Equals(x.Password,userModel.Password));
    }
}

The service exposes a method called GetUserModel which would accept a POCO and return the a User Dto representing User with same user name (and password – effectively validating)mentioned in the POCO. The UserModel and UserDto are defined as

public record UserModel
{
    [Required]
    public string UserName { get; set; }

    [Required]
    public string Password { get; set; }
}

public record UserDto(string UserName,string Password,string Role)
{
}

Withour services in place, it is time to ensure we have ability to inject them into our controllers. We do this by adding into our ServiceCollection using the ConfigureServices() method.

services.AddTransient<ITokenService, TokenService>();
services.AddTransient<IUserRepositoryService, UserRepositoryService>();

We can now inject them our constructor.

private readonly ITokenService _tokenService;
private readonly IUserRepositoryService _userRepositoryService;
private readonly IConfiguration _configuration;
public HomeController(ITokenService tokenService,IUserRepositoryService userRepositoryService,IConfiguration config )
{
    _tokenService = tokenService;
    _userRepositoryService = userRepositoryService;
    _configuration = config;
}

Login Action

Now we have almost everything that we require for authentication and generation of token. We have registered and configured our middlewares, and so have created service for token generation. Let us now write the Login action which would used by the clients for logging into the system.

[AllowAnonymous]
[Route("login")]
[HttpPost]
public ActionResult Login(UserModel userModel)
{
    if(string.IsNullOrEmpty(userModel.UserName) || string.IsNullOrEmpty(userModel.Password))
    {
        return RedirectToAction("Error");
    }

    IActionResult response = Unauthorized();
    var user = _userRepositoryService.GetUser(userModel);

    if(user != null)
    {
        var generatedToken = _tokenService.BuildToken(_configuration["Jwt:Key"].ToString(), _configuration["Jwt:Issuer"].ToString(), user);
        return Ok(new
        {
            token = generatedToken,
            user = user.UserName
        });

    }
    return RedirectToAction("Error");
}

private UserDto GetUser(UserModel userModel)
{
    return new UserDto("Jia", "admin", "admin");

}

Notice that we have applied the [AllowAnonymous] attribute to the action. This instructs that the action is an unprotected resource and could be accessed by an non-authenticated user. This is of course required, otherwise the user will never be able authenticate himself.

As seen in the code above, we retrieve the UserRepository Service to retrieve the User Dto using the data we received from the client. The validated user would then generated a token using the ITokenService.

var generatedToken = _tokenService.BuildToken(_configuration["Jwt:Key"].ToString(), _configuration["Jwt:Issuer"].ToString(), user);

At this point, we are also retrieving the configuration information we had stored in the AppSettings.Json in the first step, including the secret key. The Generated token is then send back to the caller.

Authorizing the Protected resources

We decorate the protected resources using the [Authorize] attribute to specify that they need authentication. For example,

[Authorize]
[Route("Secret")]
[HttpPost]
public ActionResult SecretFunction()
{
    return Ok("Alright, you are authorized user");
}

The Action Secret can be now accessed only by an authenticated user. The authentication handlers would execute a Challenge/Forbid action every time an unauthorized user attempts to access a protected resource.

That was a brief walk through for implementing Jwt authentication using .Net Core 5. The code in the post could can be found in my github here. The complete process can be summarized as the following.

JWT Authentication

One thought on “Jwt Authentication using .Net Core 5

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s