I have recently been using JWT tokens as the PREFERRED authentication method for the API. Also, I deal with a lot of JWT token authentication and authorization, and it’s worth documenting.

1. Relevant authorization

First of all, there is a lot of documentation about using ASP.NET Identity to handle authentication/authorization, so when you follow the previous MVC authorization scheme and use the UserManager series tables and class libraries, you can enjoy the built-in attributes in the class library, such as [Authorize], etc.

However, I always run into customization, and off-the-shelf components don’t provide that flexibility. If you are having trouble with this aspect, I hope this article can help you solve some problems!

2. Create JWT tokens in ASP.NET Core

Let’s first look at how to manually create a JWT token. For our example, we will simply create a service that will return the token as a string.

You will then return the token (header, response body, and so on) of your own choosing.

I’ll also point out in the following examples that we have something like hard-coded “secret keys”. I’m doing this for demonstration purposes, but obviously, you’ll want to be able to drive by configuration. Take that as a starting point and modify it into production-ready code.

The code to generate the JWT token looks like this:

public string GenerateToken(int userId)
{
    // key key
	var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%";
	var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));

	var myIssuer = "http://codeex.cn";
	var myAudience = "http://webmote.net";

	var tokenHandler = new JwtSecurityTokenHandler();
	var tokenDescriptor = new SecurityTokenDescriptor
	{
		Subject = new ClaimsIdentity(new Claim[]
		{
			new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
		}),
		Expires = DateTime.UtcNow.AddDays(7),
		Issuer = myIssuer,
		Audience = myAudience,
		SigningCredentials = new SigningCredentials(mySecurityKey, SecurityAlgorithms.HmacSha256Signature)
	};

	var token = tokenHandler.CreateToken(tokenDescriptor);
	return tokenHandler.WriteToken(token);
}
Copy the code

Let’s walk through it step by step.

There is a security key that is essentially used to “sign” the token. When we receive the token on the other end, we can verify this signature to ensure that it was created by us.

The token itself is actually readable, even if you sign it, so don’t put sensitive information in it. The signature only verifies that we created the token and that the token has been tampered with, but it does not “encrypt” the token.

Issuer and Audience are interesting things because, in practice, they may not be very useful. The issuer who created the token, such as your website, and the audience who used the token. So a good example might be that when a user logs in, your authentication API (auth.codeex.com) will be the issuer, and your generic API will be the intended audience (api.codeex.com).

These are actually free text fields, so they don’t need to be specified, but we’ll need to know what they are later when we validate the publisher/audience.

We’ll create a 7-day token, but you can set it to whatever value you want (or make it completely unexpired), and the rest of the code is just. NET Core specific token writing code.

3. Identity statement

Declarations are actually a simple concept, but there is a lot of “abstract” thinking around them.

Simply put, a statement is a “fact” stored in a token about the user/person holding the token.

For example, if I log on to my site as an administrator, my token might “claim” that I am an administrator.

Or write in one sentence “The person holding this token may claim that they are the administrator.”

Just as you can store arbitrary information in cookies, you can essentially do the same in JWT tokens.

For example, we could do the following:

Subject = new ClaimsIdentity(new Claim[]
{
	new Claim("UserRole"."Administrator"})),Copy the code

Note that instead of using the “ClaimTypes” static class as we did in the first example, we just use an arbitrary string to define the declaration name and then say what the value is.

You can do this for basically any information you need, but remember again that anyone can decode the JWT token, so you should not store any sensitive information in it.

Another great way is to store the declared type as static const/readonly. Such as:

public static readonly string ClaimsRole = "UserRole";

[.]

Subject = new ClaimsIdentity(new Claim[]
{
	new Claim(ClaimsRole, "Administrator"})),Copy the code

You may need to use the ClaimType string in more than one place, so it is best to set it as a static variable.

4. Validate the token

Once you have created the token, the next step is to authenticate the user when they send it to you.

Now I personally like to send it in a header like X-ApI-Token, but since it’s just a string, you can send it any way you like. Here’s an example.

public bool ValidateCurrentToken(string token)
{
	var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%";
	var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));

	var myIssuer = "http://codeex.cn";
	var myAudience = "http://webmote.net";

	var tokenHandler = new JwtSecurityTokenHandler();
	try
	{
		tokenHandler.ValidateToken(token, new TokenValidationParameters
		{
			ValidateIssuerSigningKey = true,
			ValidateIssuer = true,
			ValidateAudience = true,
			ValidIssuer = myIssuer,
			ValidAudience = myAudience,
			IssuerSigningKey = mySecurityKey
		}, out SecurityToken validatedToken);
	}
	catch
	{
		return false;
	}
	return true;
}
Copy the code

Notice that I had to copy and paste the security key, issuer, and visitor into this method. As usual, configuration is better in configuration classes.

What is the principle of verification?

It’s actually quite simple. We create a TokenHandler, which is used to handle JWT tokens. NET Core built-in class to which we pass and authenticate tokens and “intended” publisher, audience, and security keys. This verifies that the issuer and the audience meet our expectations and sign the token with the correct key. If the token is not validated, an exception is thrown, so we can simply catch this exception and return false.

5. Get an identity statement

The final piece of the puzzle is getting the declaration. This is actually quite easy, assuming we’ve already verified the token itself.

public string GetClaim(string token, string claimType)
{
	var tokenHandler = new JwtSecurityTokenHandler();
	var securityToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

	var stringClaimValue = securityToken.Claims.First(claim => claim.Type == claimType).Value;
	return stringClaimValue;
}
Copy the code

6. Add AddAuthentication/AddJwtBearer?

You may have read the documentation that uses the following code:

ervices.AddAuthentication(x =>
{
	x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
	x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
	x.TokenValidationParameters = new TokenValidationParameters();
});
Copy the code

Or some variation of it to set token authentication parameters with a signing key, audience, and publisher.

This method is valid only if the default Authorize attribute is used. These Settings are a way to configure the authorization handlers built into ASP.NET Core. If you create/validate the JWT token yourself, it does not set any other global Settings.

7, summary

It doesn’t look like much. It’s easy.