I’ve been tinkering with different options to secure the API endpoint of one of my Asp.Net Core apps. What I end up using was IdentityServer4, primarily because my app needed Single sign-on too. But for straightforward scenarios, I think IdentityServer4 is an overkill. That’s where JWT comes in, so in this post, I’m going to share what I’ve learned using JWT in Asp.Net Core 3.1. In my next post I’m going to do the same thing with IdentityServer4.
So let’s say I have an Insecure API like this.
Our goal is to implement policy-based authorization that uses the built in Asp.Net Core Identity claim. By doing so only people who fit a particular policy can use this API. In this example, only people who has the claim “Employee” with the value “Mosalla” should be able to access our API. I’m going to explain what I did step by step, and give you the sample code in the end.
Adding JwtBearer To Startup.cs
The first step we need to take is to add the JwtBearer in ConfigureServices
part of our Asp.Net Core app. There is no need for additional package, JWT is now part of Asp.Net Core authentication and lives in AspNetCore.Authentication.JwtBearer
namespace. We do this by using the AddJwtBearer
extension method, here the complete code for Startup.cs
.
The AddJwtBearer
has some options which I’ve used. The first option is RequireHttpsMetadata
which determines if HTTPS is required. This should always be true for production environments, I only turned it off because I wanted to make things easier during development. The reason this is important is the nature of Bearer tokens. Which means whoever carry this token, can have access to our resources. Any system that implement JWT grants access to whoever has the token. It doesn’t care who owns this token, that is the identity of the owner, only the token’s validity. So it’s imperative to use SSL to make sure our tokens are not stolen while travel across the wire.
The TokenValidationParameters
property can help us substantiate the validity of the incoming token. By setting this property the issuer, audience and sign in key are added to the every token that our system is going to generate.
Also on line 53 I’ve declared a policy called Trusted
which which I’m going to use to secure our API. Now we can secure our API by adding authorize attribute and our policy to it. Also note that we should instruct Asp.Net Core to use AuthenticationSchemes
of JwtBearerDefaults.AuthenticationScheme
. You could either set your DefaultAuthenticateScheme
in AddAuthentication
options in startup, or set it explicitly on our action method. The reason I didn’t set it in startup is that I wanted to use JWT alongside cookie authentication. By doing so I have to explicitly set the AuthenticationSchemes
whenever I want to use the JwtBearer to secure my API action methods.
The next step is creating a class to generate access tokens.
Generating JWT Access Token
Now what we need is some kind of endpoint to create access token if the provided credentials where valid.
We first check to see if the incoming credentials are valid, if they are, we read the user claims. After that we add the JWT related tokens to the list of user’s claim. Next we create an instance of SymmetricSecurityKey
and SigningCredentials
and pass them along with user’s claims to JwtSecurityToken
constructor to create a JWT access token. Now people who want to access our API needs to make a request to Token/Generate.
If their credential were valid they receive an access token. They should use that access token to set the HTTP Authorization
header of their request. Finally they can send their request to the secured API. As far as securing our API goes, we’re done. Next step is to make a request to that API with the Username and Password of the user that we’ve created to see if it works correctly.
Create User And Add Claim For it
Now we need to register a user and add the necessary claim to it. Once user registered and logged in, I’ve added a link which navigates us to the action that adds the claim.
Normally we shouldn’t use a trivial get method to change state in our system, but for this example it suffice.
Call The API With Our Credentials
Now we need to make a request to the API, we do this with HttpClient
, consider the following code.
First we need to get an access token, we do this by calling GetAccessToken
and this method in turn create an HttpContent
that PostAsync
needs by creating an instance of StringContent
class. We pass our Username and Password along with encoding and media type for our post request. We make the request and return the access token. Now that we have a access token we can set the DefaultRequestHeaders.Authorization
property of our HttpClient
. Here we pass Bearer
for our Authentication Scheme
, and our access token. Here’s what we’ll get if the call to our API was successful.
Now let’s enter the wrong password to see what we’ll get.
Note that not only our Username and Password should be correct, but also our user should fit the Trusted policy that we set earlier. So it means if your delete the user’s claim from AspNetUserClaims
table, our request is not authorized.
Summary
In this post, I’ve explained how we can use JWT with Asp.Net Core 3.1 to implement policy-based authorization for our API. You can find the sample project for this post here. This post was part of a series of posts which I intend to write, in my next post I’ll show you how to do the same thing using IdentityServer4.
Isn’t this supposed to apply as default? and skip specifying the schema in the Authorize attribute?
(this is what i’ve supposeed that it does but i didn’t get it working either)
/*o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}*/ /*Uncomment this if you don’t want to use JWT for all of your api*/
As far as I can remember you can either set the schema in startup and this will be used across the project, or you can set it via attribute on action by action cases.
Hi,
I am using CORS and my
token got generated but when I am trying to access api it is throwing me error-Token-invalid.Singature key was not found.
I have given issuersigningkey already.
om localhost its working but om server I am getting this error.