Common Errors in JWT Bearer Authentication

cover

When implementing JWT Bearer authentication in an ABP(tiered) application, you might occasionally encounter errors starting with IDX. These errors are related to JWT Bearer Token validation and this article will help you understand and resolve them.

Enable JWT Bearer authentication

Your API project usually contains the following code, which enables JWT Bearer authentication and makes it as the default authentication scheme.

We simply configure the JWT's Authority and Audience properties, and it will work fine.

context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
	.AddJwtBearer(options =>
	{
		options.Authority = "https://localhost:44301/"; //configuration["AuthServer:Authority"];
		options.Audience = "MyProjectName";
	});

AddJwtBearer and AddAbpJwtBearer will do the same thing, but AddAbpJwtBearer is recommended.

JWT authentication process

Let's take a look at how the above code works.

A JWT Token usually consists of three parts: Header, Payload, and Signature.

  • Header: Contains the type and signing algorithm of the token
  • Payload: Contains the claims of the token, including sub, aud, exp, iat, iss, jti, preferred_username, given_name, role, email, etc.
  • Signature: The cryptographic signature of the token used to verify its authenticity

Here is an example of a JWT Token issued by AuthServer(OpenIddict):

The Header part:

JWT Header

The Payload part:

JWT Payload

TokenValidationParameters

In the JwtBearerOptions, there is a TokenValidationParameters property, which is used to validate the JWT Token.

The default implementation for JWT Token validation is JsonWebTokenHandler, which comes from the Microsoft.IdentityModel.JsonWebTokens package.

We didn't set the TokenValidationParameters property in the code above, so the default values below will be used:

//...
TokenValidationParameters.ValidateAudience = true
TokenValidationParameters.ValidAudience = "MyProjectName"
TokenValidationParameters.ValidAudiences = null

TokenValidationParameters.ValidateIssuer = true
TokenValidationParameters.ValidIssuer = null
TokenValidationParameters.ValidIssuers = null
//...

JWT Bearer Token Validation Process

During JWT Bearer authentication, API website will get the token from the HTTP request and validate it.

The JsonWebTokenHandler will get the OpenID Connect metadata from the AuthServer, it will be used in the validation process, the current metadata request address is: https://localhost:44301/.well-known/openid-configuration , it is a fixed address calculated from the Authority property.

First, the token's Signature is verified using the public key obtained from OpenID Connect metadata(https://localhost:44301/.well-known/jwks).

Then, the payload is validated. The payload is a JSON object containing essential information such as the token type, expiration time, issuer, and audience etc.

Most of the validation problems we may encounter are payload validation failures, for example:

Lifetime

If the token in your request has expired, the validation will fail. You will see the exception information like IDX10230 in the log.

Audience

The ValidAudience of TokenValidationParameters is MyProjectName, the aud in the payload of the token is also MyProjectName, if the token does not contain aud or the aud does not match, the validation will fail. You may see the exception information like IDX10206, IDX10277 or IDX10208.

If the ValidateAudience of TokenValidationParameters is false, then the aud will not be validated.

Issuer

The default value of TokenValidationParameters.ValidateIssuer is true, it requires the token's payload to contain the issuer field, and it must match one of TokenValidationParameters.ValidIssuer or TokenValidationParameters.ValidIssuers.

The default value of ValidIssuer or ValidIssuers is null, it will use the issuer from the OpenID Connect metadata as the default value.

  1. If the token's payload does not contain the issuer field, you may see the error IDX10211.
  2. If the API website cannot get the OpenID Connect metadata from AuthServer website, the validation will fail. You may see the error IDX10204, the full exception message is: IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.
  3. If the issuer does not match, the validation will fail. You may see the error IDX10205 in the log.

If the ValidateIssuer of TokenValidationParameters is false, then the issuer will not be validated.

Please note that OpenIddict will use the current HTTP request information as the value of issuer. If the AuthServer website is deployed behind a reverse proxy or similar deployment configurations, the issuer in the token may not be the value you expect. In this case, please specify it manually.

PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
	serverBuilder.SetIssuer("https://localhost:44301/");
});

Troubleshooting

To troubleshoot any IDX errors during JWT authentication, you can enable detailed logging by configuring the identitymodel logs as follows:

using System.Diagnostics.Tracing;
using Microsoft.IdentityModel.Logging;

public class Program
{
    public async static Task<int> Main(string[] args)
    {
        IdentityModelEventSource.ShowPII = true;
        IdentityModelEventSource.Logger.LogLevel = EventLevel.Verbose;
        var wilsonTextLogger = newTextWriterEventListener("Logs/identitymodel.txt");
        wilsonTextLogger.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Verbose);

		//...
    }
}

Additionally, you can enable OpenIddict's Verbose logs for more detailed debugging information:

var loggerConfiguration = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
    .MinimumLevel.Override("OpenIddict", LogEventLevel.Verbose)
    .Enrich.FromLogContext()
    .WriteTo.Async(c => c.File("Logs/logs.txt"))

Summary

For JWT authentication, you need to pay attention to the following key points:

  1. Ensure your API website can communicate with the AuthServer properly
  2. Verify that the aud claim in your token matches the expected audience
  3. Confirm that the issuer claim in your token is valid and matches the configuration

You can customize the JwtBearerOptions's TokenValidationParameters to modify the validation rules to meet your actual needs.

For example, if your issuer needs to support multiple subdomains, you can use the Owl.TokenWildcardIssuerValidator library to customize the validation.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://abp.io";
        options.Audience = "abp_io";

        options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
        options.TokenValidationParameters.ValidIssuers = new[]
        {
            "https://{0}.abp.io"
        };
    });

References