developerlife.pl

Web & .NET development, business and my life

JWT Authorization for ASP .NET Core Web API

Almost every web application uses authorization.

In the post I would like to show you how you can implement simple authorization with JSON Web Token in easy way.

JWT is based on JSON standard. There is possible to create and to read tokens which contains information about claims. With JWT you can sing in to application with user credentials and receive authorization token as a response which you can use to connect with some API.

Example

JSON Web Token imnplementation with .NET Core

There is no problem to implement JWT tokens with .NET Core. The framework contains required libraries by default. In the below example I created application with dotnet core 2.1.5.

Let’s move to code.

First of all, create a project and add web api application

dotnet new sln -n JWTExample
dotnet new webapi -n JWTExample.Web -o Web/JWTExample.Web
dotnet sln JWTExample.sln add Web\JWTExample.Web\JWTExample.Web.csproj

Create required classes

At the beginning create JWTConfiguration class, which contains settings, UserIdentity class as a model for credentials and AccessToken class returned by authorization service. I created these files in Models directory.

  • JWTConfiguration.cs

    public class JWTConfiguration
    {
        public string SecretKey { get; set; }
        public string ValidIssuer { get; set; }
        public string ValidAudience { get; set; }
        public int TokenExpirationTime { get; set; }
    }
    
  • UserIdenity.cs

    public class UserIdentity
    {
        public string Login { get; set; }
        public string Password { get; set; }
    }
    
  • AccessToken.cs

    public class AccessToken
    {
        public DateTime ExpireOnDate { get; set; }
        public long ExpiryIn { get; set; }
        public string Token { get; set; }
        public bool Success { get; set; }
    }
    

Please write configuration in appsettings.json file

{
    "Logging": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "AllowedHosts": "*",
    "JwtConfiguration": {
      "SecretKey": "B61D7543-8584-43B7-AV3B2-B319219AF0DA", // some example secret key
      "ValidIssuer": "C5D3A460-AF5F-33FF-9600-300460774FF9", //some example valid issuer
      "ValidAudience": "621B62B0-83C2-KK57-BEAF-9362D3D31EC5", // some example audience
      "TokenExpirationTime": "15" // expiration token time in minutes
    },
    "UserIdentity": {
      "Login": "developerlife",
      "Password": "7c7d6a040a402039473269df719e06b1" // hashed password with md5 - aziemianski
    }
}

Go to the Startup.cs and inject configuration to service collection

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient((config) =>
    {
        var conf = new JWTConfiguration();
        Configuration.GetSection("JWTConfiguration").Bind(conf);
        return conf;
    });
    services.AddTransient((config) =>
    {
        var conf = new UserIdentity();
        Configuration.GetSection("UserIdentity").Bind(conf);
        return conf;
    });
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Configure JWT like below

  1. Create ConfigureJWT method

    public void ConfigureJwt(IServiceCollection services)
    {
        var config = services.BuildServiceProvider().GetService<JWTConfiguration>();
        var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(config.SecretKey));
        var tokenValidationParameters = new TokenValidationParameters
        {
            IssuerSigningKey = signingKey,
            ValidIssuer = config.ValidIssuer,
            ValidAudience = config.ValidAudience
        };
        services.AddAuthentication(o =>
        {
            o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(c =>
        {
            c.RequireHttpsMetadata = false;
            c.SaveToken = true;
            c.TokenValidationParameters = tokenValidationParameters;
        });
    }
    
  2. Invoke the method on the end of ConfigureServices method

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        ConfigureJwt(services); // there is new line
    }
    
  3. Modify Configure method

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ...
        app.UseAuthentication(); // there is new line
        app.UseHttpsRedirection();
        app.UseMvc();
    }
    

Prepare an API to generate access token.

  1. Add an AuthController.cs file in the Controllers directory

  2. Please add POST method which names GenerateToken. The method as argument has UserIdentity and as a return type AccessToken. The Route attribute set with “token” value. Inject UserIdentity and JWTConfiguration in the class constructor.

    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly UserIdentity userIdentity;
        private readonly JWTConfiguration configuration;
    
        public AuthController(UserIdentity userIdentity, JWTConfiguration configuration)
        {
            this.userIdentity = userIdentity;
            this.configuration = configuration;
        }
    
        [HttpPost]
        [Route("token")]
        public AccessToken GenerateToken([FromBody]UserIdentity credentials)
        {
            
        }
    }
    
  3. Prepare method to create hashed password

    private static string GetMd5Hash(MD5 md5Hash, string input)
    {
        byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < data.Length; i++)
        {
            sBuilder.Append(data[i].ToString("x2"));
        }
        return sBuilder.ToString();
    }
    
  4. Now please implement the earlier prepared method.

    [HttpPost]
    [Route("token")]
    public AccessToken GenerateToken([FromBody]UserIdentity credentials)
    {
        string md5Password = string.Empty;
        using (MD5 md5Hash = MD5.Create())
        {
            md5Password = GetMd5Hash(md5Hash, credentials.Password);
        }
    
        if (userIdentity.Password != md5Password || userIdentity.Login != credentials.Login)
        {
            Response.StatusCode = StatusCodes.Status401Unauthorized;
            return new AccessToken { Success = false };
        }
    
        var claims = new[]
        {
                new Claim(JwtRegisteredClaimNames.Sub, credentials.Login),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };
    
        var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration.SecretKey));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var expiredOn = DateTime.Now.AddMinutes(configuration.TokenExpirationTime);
        var token = new JwtSecurityToken(configuration.ValidIssuer,
              configuration.ValidAudience,
              claims,
              expires: expiredOn,
              signingCredentials: creds);
    
        return new AccessToken
        {
            ExpireOnDate = token.ValidTo,
            Success = true,
            ExpiryIn = configuration.TokenExpirationTime,
            Token = new JwtSecurityTokenHandler().WriteToken(token)
        };
    }
    

Congratulation! The authorization API is done.

In the next step please add a TodoController. The controller is for tests because we want to check if our solution works well.

[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
    [Authorize]
    public IList<string> GetValues()
    {
        var someClaim = this.HttpContext.User.Claims.FirstOrDefault();
        return new List<string>
        {
            "Example of JWT auth for https://developerlife.pl",
            $"User: {someClaim.Value}"
        };
    }
}

Aplication tests

You will used Postman tool to test your application. At the beginning let’s try invoke the service without token

As you probably suppose the service returns 401 status. So now let’s generate required token.

To the first request please add received token to headers with Authorization key. Before token add information about token type. In this case is Bearer.

In the response you can see correct results.

Good job! You created your own API with .NET Core framework and C# language.

Now you can implement connection with your web application in safe way via web client, mobile application, IoT etc.

I hope the article was clear for you. You can get the application code from my repository.

Leave a Reply

Your email address will not be published. Required fields are marked *