![ASP.NET Core JWT Authentication and Authorization of Web API [Detailed] ASP.NET Core JWT Authentication and Authorization of Web API [Detailed]](https://www.yogihosting.com/wp-content/uploads/2020/03/JWT-JSON-Web-Token.png)
Page Contents
What is JWT in .NET – JWT which stands for “JSON Web Tokens” is an open standard method for securely transmitting information between parties as a JSON object (commonly known as ‘Token’). The token is digitally signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA. Since the token is signed with a public/private key pairs, the signature certifies that only the party holding the private key is the one that signed it. The JWT is generated by the authentication server when a client successfully logs in. This token contains the user’s identity and access rights and is then sent with every API request (which the client makes) as a bearer token in the authorization header.
In the below image I have shown a JWT Token which looks like xxxxx.yyyyy.zzzzz.

A JWT Web Token consists of 3 parts separated with 2 dots (header.payload.signature). These parts are:
Note: the signature doesn’t identify the sender by itself — identity comes from the claims in the payload (e.g., sub). What the signature confirms is that those claims are trustworthy, because they were signed by someone holding the correct key.
Copy the token and paste it on the token box given at https://jwt.io/. This will decode your JWT token and show the header, payload and signature in JSON. On the “payload” json, copy the value of “exp” key. This is the expiry time of the token expressed in Unix time. Now use C# function DateTimeOffset.FromUnixTimeSeconds(unix-time) to convert the unix time to an understandable date and time value. I have shown this in the below image:

This tutorial is a part of series called JSON Web Token (JWT) in ASP.NET Core. There are 3 tutorials to master it:
The implementation is as follows.
Authorization header (as a Bearer token) whenever it makes a request to a secured resource.
There are 2 steps to use jwt authentication with web api. Step 1: Add configurations on the Program class to use JWT authentication. Step 2: Add the [Authorize] attribute on the Web API controller. This will secure it with JWT authentication.
Let us create a JWT example to create Web API Security feature. This ensures that clients must send JWT token in the HTTP Authorization Header in order to access data from the API.
In Visual Studio, create a new ASP.NET Core MVC app and name it JWTAPI.

Next, select .NET 10.0 framework from the dropdown and create the app.

You need to install the package called Microsoft.AspNetCore.Authentication.JwtBearer from NuGet. This package adds the middleware that enables an ASP.NET Core application to receive a Bearer Token in the Request Pipeline.

Next, configure JWT authentication in the project. To do this, register a JWT authentication schema by using AddAuthentication() method in the Program.cs file. The code which you have to add is given below:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "dotnetclient",
ValidIssuer = "https://www.yogihosting.com",
ClockSkew = TimeSpan.Zero,// It forces tokens to expire exactly at token expiration time instead of 5 minutes later
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007_MynameisJamesBond007"))
};
});AddAuthentication() registers the authentication services and tells the app which scheme to use by default. JwtBearerDefaults.AuthenticationScheme is just the string “Bearer”.
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})Since we are only using one scheme (JWT), setting all three to JwtBearerDefaults.AuthenticationScheme just makes it explicit and unambiguous.
Next, I used the AddJwtBearer() method to register the actual JWT Bearer handler and configures how tokens are validated.
.AddJwtBearer(options =>
{
...
});I then have added:
options.SaveToken = true;It stores the validated token in the AuthenticationProperties after a successful authentication, so I can retrieve the raw token later in the code (e.g., via HttpContext.GetTokenAsync("access_token")) — useful if I need to forward the token elsewhere.
options.RequireHttpsMetadata = false;Normally, the JWT handler requires the token issuer’s metadata endpoint to be HTTPS. Setting this to false disables that requirement — this is only meant for local development/testing over HTTP. ⚠️ In production, this should be true (or left as default), since disabling it weakens transport security guarantees.
I used the TokenValidationParameters property to specify how the token will be validated on the server.
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
...
};I also set true for the Issuer and Audience.
ValidateIssuer = true,
ValidateAudience = true,The iss claim is embedded inside the token by the JWT server at the time of creation, identifying who issued it. ValidIssuer is the value the server checks that claim against — here, "https://www.yogihosting.com". If ValidateIssuer = true, a request is only authenticated if the token’s iss claim matches ValidIssuer exactly. You can set this to whatever value fits your issuer.
Similarly, the aud claim inside the token identifies who the token is intended for. ValidAudience is the value the server checks it against — here, "dotnetclient". If ValidateAudience = true, the token is only accepted if its aud claim matches ValidAudience.
ValidAudience = "dotnetclient",
ValidIssuer = "https://www.yogihosting.com",The most important property here is IssuerSigningKey, set using the SymmetricSecurityKey class. This is a shared secret (not a private key — that term applies only to asymmetric algorithms like RSA/ECDSA). The server uses this key to sign the token when it’s created, and the same key is used by the server to validate the token’s signature when it receives it in the request header. The client never signs anything — it just stores the token and sends it back as-is.
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007_MynameisJamesBond007"))If the key used to validate the token doesn’t match the key used to sign it, signature verification fails, and the request to the protected resource is rejected.
I’m using 'MynameisJamesBond007_MynameisJamesBond007' as the key. For HS256, .NET requires a minimum of 16 characters (128 bits) — but for stronger security, a 32+ character (256-bit) key is recommended.
ClockSkew = TimeSpan.Zero,
ClockSkew controls how much leeway the token validation allows for time differences between servers when checking the exp (expiration) claim. With ClockSkew = TimeSpan.Zero, the buffer is removed entirely. The token is considered expired the exact instant its exp time passes — no grace period at all.
ClockSkew = TimeSpan.Zero,Next, tell the app to use authentication and authorization. You add the below 2 lines of code after the app.UseRouting().
app.UseAuthentication();
app.UseAuthorization();Some related API tutorials which you need to read are:
I will create the API method to return data to the client in JSON. This data will be a list of flight reservations. The Web API will also have JWT Authentication added to it. So, create a new class called Reservation.cs, preferably inside the Models folder, and add to it the code given below:
public class Reservation
{
public int Id { get; set; }
public string Name { get; set; }
public string StartLocation { get; set; }
public string EndLocation { get; set; }
}
Next, create a new controller called ReservationController.cs. This controller will have the task to return reservations to the client whenever request to the API is made.
The full code of this controller is given below:
using JWTAPI.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace JWTAPI.Controllers
{
[Route("[controller]")]
[ApiController]
[Authorize]
public class ReservationController : ControllerBase
{
[HttpGet]
public IEnumerable<Reservation> Get() => CreateDummyReservations();
public List<Reservation> CreateDummyReservations()
{
List<Reservation> rList = new List<Reservation> {
new Reservation { Id=1, Name = "Ankit", StartLocation = "New York", EndLocation="Beijing" },
new Reservation { Id=2, Name = "Bobby", StartLocation = "New Jersey", EndLocation="Boston" },
new Reservation { Id=3, Name = "Jacky", StartLocation = "London", EndLocation="Paris" }
};
return rList;
}
}
}
Explanation : I have marked this controller with the [Authorize] attribute from the Microsoft.AspNetCore.Authorization namespace. This secures the API with JWT authentication: if the token is missing or invalid, the Web API methods will not be executed, while a valid token allows the request to proceed.
I have created some dummy reservations inside the CreateDummyReservations () function and these are returned by the API in JSON. The HttpGet method of the API does this work of returning these reservations in JSON.
[HttpGet]
public IEnumerable<Reservation> Get() => CreateDummyReservations();With this setup, you have now successfully secured your API with JWT. Now request from the clients will be authenticated beforehand and only then the Web API will provide them with the reservations data. In technical terms – the Web API will authenticate the request from the clients and if these requests contain a correct JWT token then only the Web API will authorize the request to view the reservations.
Let us perform some testing. Comment out the [Authorize] attribute on the ‘ReservationController’ by applying ‘//’ before it. Next, run your application on visual studio. When the browser starts, open the URL of the ‘ReservationController’ which in my case is:
https://localhost:7154/ReservationYou will see the JSON containing the reservations as shown below:

Now remove the ‘//’ before the [Authorize] attribute (i.e. un-comment it) and refresh the same URL. This time you will see Http Error 401 which tells – you are unauthorized to access the reservation controller. Congratulations, JWT is implemented successfully on your JWTAPI project.

We have done the JWT Configuration on our project. Let’s now create the login feature.
We need to install the System.IdentityModel.Tokens.Jwt package. This package is used to create, serialize, and validate JWT tokens. Install it from the NuGet Package Manager Console with Install-Package System.IdentityModel.Tokens.Jwt, or check the screenshot from NuGet below.

We are ready to create JWT Token but we also need a place to store this token. Normally websites store the token in a database or in a cookie. Here we will store the token in a JWT Cookie. This cookie is just a normal cookie which will be storing the JWT Token. Whenever we need to call the web api, we will read this cookie to get the JWT Token and add this token on the authorization header of the request.
Now you are ready to make API Calls. So create a new controller called CallAPIController.cs.
We will also need to install Newtonsoft.json package which will convert the json returned by the web api to List<Reservation>. You can install it by running the command on the Package Manager Console window – Install-Package Newtonsoft.Json.
Now, add Index action methods which will validate for the username and password. Recall the JWT Token is created only when the user is successfully validated on the login screen. So when user is successfully logged in, a JWT Token is created and stored in a JWT Cookie. The code for this is shown in provided below:
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
namespace JWTAPI.Controllers
{
public class CallAPIController : Controller
{
public IActionResult Index(string message)
{
ViewBag.Message = message;
return View();
}
[HttpPost]
public IActionResult Index(string username, string password)
{
if ((username != "secret") || (password != "secret"))
return View((object)"Login Failed");
var accessToken = GenerateJSONWebToken();
SetJWTCookie(accessToken);
return RedirectToAction("FlightReservation");
}
private string GenerateJSONWebToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007_MynameisJamesBond007"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "https://www.yogihosting.com",
audience: "dotnetclient",
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private void SetJWTCookie(string token)
{
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddHours(3),
};
Response.Cookies.Append("jwtCookie", token, cookieOptions);
}
}
}
Explanation: To successfully login we need to enter “secret” for both username and password. You can replace this code with something that checks the username and password of the user from the database using Entity Framework Core. It’s code would be similar to this:
var user = await _db.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null)
return Unauthorized();
var result = _hasher.VerifyHashedPassword(user, user.PasswordHash, password);
if (result == PasswordVerificationResult.Failed)
return Unauthorized();Next, see the code – var accessToken = GenerateJSONWebToken();. Here a function is called to create a new JWT Token once user successfully logs in. It also assigns the values of issuer, audience, expires and signingCredentials on the token. Next, SetJWTCookie(accessToken); calls a function which will set this JWT Token inside a cookie.
Note the values of issuer and audience which are the same which we added on the program class earlier.
issuer: "https://www.yogihosting.com",
audience: "dotnetclient",
We use JwtSecurityToken class “expires” property to set the expiry time of the JWT Token. Example: to set the expirty time of the token to be 3 hours after it is created, the C#.NET code for this will be expires: DateTime.Now.AddHours(3). I have shown this in highlighted way in the below code:
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "https://www.yogihosting.com",
audience: "dotnetclient",
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials
);
We can also use DateTime.Now.AddMinutes to set expiry time in minutes or DateTime.Now.AddSeconds to set the time in seconds.
The JWT Token is returned by the GenerateJSONWebToken() function and the variable accessToken stores it. The function SetJWTCookie() creates a cookie by the name of “jwtCookie” and stores the JWT Token on it.
Notice the JWT token expires after 3 hours and the cookie also expires in 3 hours. We will talk more about the expiry time in just a moment. Next, I will adds the JWT Token to the HTTP authorization header.
Add a new action method called “FlightReservation” to the CallAPIController.cs controller class. Here we will make the call to the Web API with JWT Token added to the HTTP authorization header. This action method code is given below.
public async Task<IActionResult> FlightReservation()
{
var jwt = Request.Cookies["jwtCookie"];
List<Reservation> reservationList = new List<Reservation>();
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
using (var response = await httpClient.GetAsync("https://localhost:7154/Reservation")) // change API URL to yours
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string apiResponse = await response.Content.ReadAsStringAsync();
reservationList = JsonConvert.DeserializeObject<List<Reservation>>(apiResponse);
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
return RedirectToAction("Index", new { message = "Please Login again" });
}
}
}
return View(reservationList);
}I first read the jwtCookie value which contains the JWT token.
var jwt = Request.Cookies["jwtCookie"];Next, with HttpClient class, I am calling the Web API. Notice I am adding the JWT Token to the authorization header with the below code line.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenString);And finally making the call to the Web API located at the URL – https://localhost:7154/Reservation.
The API response can be of 2 types:
For the HttpStatusCode.OK case, I deserialized the response into a List<Reservation>, which is sent to the view and displayed in a table. For the HttpStatusCode.Unauthorized case, the user is redirected to the login page with the message “Please login again” passed in the route, and the view displays this message.
Create, Index.cshtml razor view inside the Views/CallAPI folder. This view will ask the user to enter his/her username and password. See its code:
@{ ViewBag.Title = "Login";}
@model string
<h2>Login</h2>
@if (Model != null)
{
<div class="alert-danger">Login Failed</div>
}
@if (ViewBag.Message != null)
{
<div class="alert-danger">@ViewBag.Message</div>
}
<form method="post">
<table class="w-25 table table-striped table-bordered">
<tbody>
<tr>
<td>Username</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>Password</td>
<td><input type="text" name="password" /></td>
</tr>
<tr>
<td colspan="2">
<button>Submit</button>
</td>
</tr>
</tbody>
</table>
</form>
Next, add FlightReservation.cshtml view inside the same Views/CallAPI folder. In this view we will be displaying the reservations which we get from the Web API. It’s code is given below:
@model IEnumerable<Reservation>
@{ ViewBag.Title = "All Reservations";}
<h2>All Reservations</h2>
<table class="table table-sm table-striped table-bordered m-2">
<thead><tr><th>ID</th><th>Name</th><th>Start Location</th><th>End Location</th></tr></thead>
<tbody>
@if (Model != null)
{
foreach (var r in Model)
{
<tr>
<td>@r.Id</td>
<td>@r.Name</td>
<td>@r.StartLocation</td>
<td>@r.EndLocation</td>
</tr>
}
}
</tbody>
</table>Perfect, now it’s now time to test it.
Coming to the testing part, run your app in visual studio. Now open the url https://localhost:7154/CallAPI which will open the Login view. Enter “secret” for both username and password.

Click the Submit button, you will be redirected to FlightReservation action which will make the API call and show all the flight reservations. Check the below image:

Recall that we set the JWT expiry time to 3 hours, and the cookie expiry time to 3 hours as well. This means we can access the secured Web API for 3 hours without needing to log in again.
Wait 3 hours and refresh the page. The JWT and the cookie have now both expired, so you will be redirected to the login page, which displays the message “Please login again”. Check the image below, where I have marked this message.

If you Don’t want to wait for 3 hours then change it to 1 minute for both jwt token and cookie and then test to see you being redirected to the login page.
expires: DateTime.Now.AddMinutes(1) // for jwt token
Expires = DateTime.UtcNow.AddMinutes(1) // for jwt cookieThe full JWT integration is completed and is working successfully. I hope you enjoyed reading this tutorial.
You can download the full codes of this tutorial from the below link:
JWT Claims are pieces of information added to the token. For example, a JWT token may contain a claim called “Roles” that asserts the Role of the user currently logged in.
First, create a Users.cs class to the “Models” folder. This class will contain the Username, Password and Roles for the users that can be logged-in to the application.
public class Users
{
public string Username { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
Now, we will modify things we created before in order to include JWT Claims. First we will change the “CallAPIController.cs” code to include JWT claims. So change login code given on the Index action method as shown in highlighted color below:
[HttpPost]
public IActionResult Index(string username, string password)
{
Users loginUser = CreateDummyUsers().Where(a => a.Username == username && a.Password == password).FirstOrDefault();
if (loginUser == null)
return View((object)"Login Failed");
var claims = new[] {
new Claim(ClaimTypes.Role, loginUser.Role)
};
var accessToken = GenerateJSONWebToken(claims);
SetJWTCookie(accessToken);
return RedirectToAction("FlightReservation");
}
The changes include checking user’s credentials from a dummy repository which is called by CreateDummyUsers() method. If we find the user in the repository then we add “Role” claim.
var claims = new[] {
new Claim(ClaimTypes.Role, loginUser.Role)
};
The “Role” claim is passed to the function which creates the JWT token.
var accessToken = GenerateJSONWebToken(claims);We now change the GenerateJSONWebToken() method code to add the claims to the JWT Token. See the change shown in highlighted color:
private string GenerateJSONWebToken(Claim[] claims)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MynameisJamesBond007_MynameisJamesBond007"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "https://www.yogihosting.com",
audience: "dotnetclient",
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials,
claims: claims
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
We also need to add CreateDummyUsers() method which holds 3 dummy users along with their roles.
public List<Users> CreateDummyUsers()
{
List<Users> userList = new List<Users> {
new Users { Username = "jack", Password = "jack", Role = "Admin" },
new Users { Username = "donald", Password = "donald", Role = "Manager" },
new Users { Username = "thomas", Password = "thomas", Role = "Developer" }
};
return userList;
}
Notice user jack has Admin role, donald has Manager role while thomas has Developer role.
We want our API to be accessed only by a specific role. We want the user to have “Manager” role, only then he can access the secured Web API. This process is know an Authorization of Roles through Claims.
We already have added the feature where the logged in users get their roles added to “Roles” claim on the token. Next we change the authorization attribute on web api to [Authorize(Roles = "Manager")] which specifies that only Manager roles claims can access the Web API. So make this change which is shown in the highlighted code given below.
[Route("[controller]")]
[ApiController]
[Authorize(Roles = "Manager")]
public class ReservationController : ControllerBase
{
[HttpGet]
public IEnumerable<Reservation> Get() => CreateDummyReservations();
public List<Reservation> CreateDummyReservations()
{
...
}
}
With this change the Web API is only accessible by user “donald” since he has “Manager” role. In the below video I have shown the login by user “jack” which does not receives any reservation data from the api as he is not in the Manager role. While when I logged in with “donald”, I receive reservations from the api.

The HttpContext.User.Claims provides all the claims added to the JWT Token. We can use it to create conditions on web api to dynamically return data based on the claims. Suppose we added a claim called “Name” to the JWT Token during the time of it’s creation.
var claims = new[] {
new Claim("Name", person)
};Next, we want to filter the records so that the record with a “Name” that is same as given on the “Name” claim is fetched. This can be done as shown below:
[HttpGet]
public IEnumerable<Reservation> Get()
{
var claims = HttpContext.User.Claims;
return CreateDummyReservations().Where(t => t.Name == claims.FirstOrDefault(c => c.Type == "Name").Value);
}JWT is a great way to secure your API without having to spend much time in integration process. I tried to show how easy the whole procedure is. I hope you like this tutorial so please share it on your reddit, facebook, twitter and other social accounts.
Hi i would be really interesting in an article with these features:
– A secured webapi (project): ASP.NET WebApi (net core 3.1) with JWT authentication / autorization an roles.
– A secured client (project): ASP.NET MVC Core 3.1 application with his own authentication and roles, which invokes the secured webapi.
– The secured client, should invoke the secured webapi, generate token and refresh tokens.
I could make a small donation if you provide a example / solution with those requirements. Pleas let me know if possible.
Hello Eduardo, Right now I don’t have any project which does this work. You can download the source code of this tutorial and will have to add these features by your own. Check the JWT authentication docs to get more info on them. Slowly and steadily you will be able to create all these features by your own.
Thank you.
Hello Yogi,
love you tutorial but i am somewhat confused in
this one…
(How to secure APIs with JWT in ASP.NET Core 3.1)
When i need to connect to a API with autentication and get a token its the api that checks my user and pass and give me a token for the session.
But in your example you put the user verification and token criation on the client side… I think you inverted things!
Hello Pedro Martins,
In the client project the call to the API is made. JWT Token is also created on the same step. Download the source codes and run the application from Visual Studio, put breakpoints on the Controller code and then debug. This way you will understand the full code very easily.
Thank you.
Hello Yogi, but shouldn´t the username validation bbeing done on the Api side and should it result in the api also supply the token to the Client?
In your example its the client that validates the username and password are correct and crates the token.
Thanks in advance,
Pedro
Hello Pedro,
Yes, you can do username validation on the API side. The client side main work is JWT Token generations done from the line
var tokenString = GenerateJSONWebToken(). So API will check this token for the request it receives.Thank you.
first of all thanku for making such of tutorial which is very helpful when i used jQuery for taking data to api nothing is happened.
Hello Rajeev,
I think you may be getting some jQuery errors please check the console of the browser for errors. Make sure the URL of the action in jQuery ajax method is correct. Also try applying breakpoint to debug.
thank you.