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.
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
-
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; }); }
-
Invoke the method on the end of ConfigureServices method
public void ConfigureServices(IServiceCollection services) { ... ConfigureJwt(services); // there is new line }
-
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.
-
Add an AuthController.cs file in the Controllers directory
-
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) { } }
-
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(); }
-
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.
Hej wielkie dzięki za wpis !
Cześć wielkie dzięki za wpis !
Cześć wielkie dzięki za wpis !
You have got remarkable knowlwdge right here.