1

I am trying to implement signed JWT (RS256) on a dotnet webapi along with KeyCloak. On app start I can see the openid calls being made to keycloak with the expected response content (requests shown below).

Get the jwks_url here

GET https://localhost:8080/auth/realms/core/.well-known/openid-configuration

Get the keys from here

GET https://localhost:8080/auth/realms/core/protocol/openid-connect/certs

I then get an access_token with the request below

POST https://localhost:8080/auth/realms/core/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=password&client_id=admin-cli&username=jim&password=foobar

I then test out the following endpoint

[ApiController]
[Route("/")]
public class AppController : ControllerBase
{
    [Authorize]
    [HttpGet]
    public OkObjectResult Get()
    {
        return Ok("This is the secured page");
    }
}

with this request

GET https://localhost:5001
Authorization: Bearer MY_TOKEN 

But I always get a 401

HTTP/1.1 401 Unauthorized
Content-Length: 0
Date: Wed, 18 Nov 2020 17:41:28 GMT
Server: Kestrel
Www-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"

The signature key (third 'chunk') does exist in the token. Below is the JWT validation code. Am I missing something?

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    var audience = Configuration["Jwt:Audience"];
    var issuer = Configuration["Jwt:Issuer"];
    bool.TryParse(Configuration["Jwt:RequireHttpsMetadata"], out var requireHttpsMetadata);

    IConfigurationManager<OpenIdConnectConfiguration> configurationManager =
        new ConfigurationManager<OpenIdConnectConfiguration>(
            $"{Configuration["Jwt:Authority"]}/auth/realms/core/.well-known/openid-configuration",
            new OpenIdConnectConfigurationRetriever());
    var openIdConfig =
        configurationManager.GetConfigurationAsync(CancellationToken.None).Result;

    services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.SaveToken = true;
            options.RequireHttpsMetadata = requireHttpsMetadata;
            options.TokenValidationParameters.IssuerSigningKeys = openIdConfig.SigningKeys;
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,

                ValidIssuer = issuer,
                ValidAudience = audience,
                ValidateIssuerSigningKey = true,
            };
        });
}
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
chris
  • 4,332
  • 5
  • 41
  • 61

1 Answers1

1

Since the JWT holds a signature (aka third chunk) I would interpret the message

"The signature key was not found"

that there is a problem with the validation of the signature.

Recheck the responses/accessibility of

GET https://localhost:8080/auth/realms/core/protocol/openid-connect/certs
monty
  • 7,888
  • 16
  • 63
  • 100
  • 1
    Using fiddler I can see the request being made and the response contains the expected keys. I have verified that the access_token is valid on jwt.io, if that is what you mean. – chris Nov 18 '20 at 20:01
  • When I previously checked the key on jwt.io I never checked it with the private key. After doing so I am given the message that it is an invalid signature. – chris Nov 18 '20 at 20:07
  • not sure if it would make the difference but may defining the authority (just for testing) could help according to this question https://stackoverflow.com/questions/58563661/bearer-error-invalid-token-the-signature-key-was-not-found – monty Nov 18 '20 at 20:10
  • For some reason when I set the authority, as is done in the link, I get 500s instead of the 401. Should the Authority be set to the same url as the Issuer? – chris Nov 18 '20 at 22:20
  • 1
    hm, you could try setting the issuersigningkey after assigning a new instance (may the get cleared). So reverse the order of the two options.TokenValidationParameters lines – monty Nov 19 '20 at 06:36
  • 1
    Moving the issuerSigningKey assignment to the end, or into the TokenValidationParams did the trick. It wasn't immediately obvious because Keycloak doesn't return an Audience claim, requiring me to also have to override the `AudienceValidator`. Thanks for the help! – chris Nov 20 '20 at 16:21