Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT Bearer Token Authorization not working after upgrade to .NET 8

I realize there are many facing issues relating to upgrading to .NET 8, and I've done my best to see if the issue I am facing is related, but none of the solutions I've seen work for me and I haven't noticed anyone else facing quite the same issue.

I am unfortunately fairly ignorant when it comes to JWT and authentication, but let me explain what I do observe happening.

The code that works with .NET 7 is something like the following:

options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero
    };

options.Authority = KeyCloakOptions.Authority;
options.MetadataAddress = $"{options.Authority}/.well-known/openid-configuration";
options.RequireHttpsMetadata = false;
options.RefreshOnIssuerKeyNotFound = true;
options.Audience = KeycloakOptions.AudienceInvoice;
options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            _logger.LogDebug("Authentication failed: " + context);
            return Task.CompletedTask;
        },
        OnTokenValidated = context =>
        {
            _logger.LogDebug("JWT Bearer Token Validated.");
            return Task.CompletedTask;
        }
    };

If I use this code, I will get an error about the issuer, something like:

IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.

This can be overcome by modifying TokenValidationParameters to include the issuer, via:

options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero,
        ValidIssuer = KeyCloakOptions.Authority
    };

I do not understand why this is now needed in .NET 8, but not in .NET 7. However, even so, after making the above change I will then get a similar error regarding the audience, something like:

IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null.

And again, this can be overcome by modifying TokenValidationParameters to include the audience, via:

options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero,
        ValidIssuer = KeyCloakOptions.Authority,
        ValidAudience = KeycloakOptions.AudienceInvoice
    };

This will lead to the next error about the signing key. And for this one, I don't know what I need to do to make it work.

My main question, though, is why I now need to include things like Issuer, Audience and Keys in the TokenValidationParameters whereas before it worked fine as part of the options. It appears that some of the options, like options.MetadataAddress are being ignored in .NET 8, which is why I now need to explicitly specify the values in TokenValidationParameters.

Or perhaps a more fundamental question, is there something that I need to add/modify so that the same (or very similar code) that worked in .NET 7 will also work in .NET 8.

Thanks for any help, Eric

like image 853
Eric Avatar asked Sep 01 '25 15:09

Eric


1 Answers

The following is working for me, which is good, though I don't have a clear understanding of why this change is necessary. The fix is essentially to move all of the validation logic into TokenValidationParameters.

That is, the code is changed from:

options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero
    };

// These properties need to be moved to TokenValidationParameters 
options.Authority = KeyCloakOptions.Authority;
options.MetadataAddress = $"{options.Authority}/.well-known/openid-configuration";
options.RequireHttpsMetadata = false;
options.RefreshOnIssuerKeyNotFound = true;
options.Audience = KeycloakOptions.AudienceInvoice;

to:

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateIssuerSigningKey = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ClockSkew = TimeSpan.Zero,
    ValidAudience = KeycloakOptions.AudienceInvoice,
    ValidIssuer = issuer,
    ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
        $"{KeyCloakOptions.Authority}/.well-known/openid-configuration",
        new OpenIdConnectConfigurationRetriever(),
        new HttpDocumentRetriever() { RequireHttps = false }
    )
};

My understanding is that this will give the equivalent behavior as that which I have now, but I would still like to understand why this change is necessary (assuming that this is the right change).

like image 119
Eric Avatar answered Sep 04 '25 05:09

Eric