XUnit Integration Testing with Identityserver: Token received from Identityserver is unable to match key

433 Views Asked by At

I'm currently trying to implement Integration Testing in an environment with 2 servers:

  • A .NET Core API server
  • An IdentityServer4 Authentication Server

Through much struggle, I've managed to get the two to communicate with eachother, however, IdentityServer throws the following error when trying to confirm the JWT token (Through the API):

Bearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key: kid: '8C1D5950D083E20D4B20DE9B37AC71FAEF679469'.

I'll try to keep the code sample brief:

In the XUnit startup, I configure and create clients for both TestServers.

    public APITestBase(ITestOutputHelper output)
    {
        _output = output;

        var apicon = new ConfigurationBuilder()
            .AddJsonFile("apisettings.json", optional: true, reloadOnChange: true)
            .Build();

        var authcon = new ConfigurationBuilder()
            .AddJsonFile("authsettings.json", optional: true, reloadOnChange: true)
            .Build();

        _authServer = new TestServer(new WebHostBuilder()
            .UseConfiguration(authcon)
            .ConfigureLogging(x => x.AddXUnit(_output))
            .UseEnvironment("Development")
            .UseStartup<Auth.Startup>());

        _authClient = _authServer.CreateClient();

        _server = new TestServer(new WebHostBuilder()
            .UseConfiguration(apicon)
            .ConfigureLogging(x => x.AddXUnit(_output))
            .UseEnvironment("Test")
            .ConfigureServices(
                  services => {
//Here, I'm adding an httpclienthandler. In the application I will use this as JWTBackChannelHandler. This allows communication between the two servers
                      services.AddSingleton<HttpMessageHandler>(_authServer.CreateHandler());
                  })
            .UseStartup<Api.Startup>());

        _client = _server.CreateClient();

        APIConfig = new Cnfg();
         
        apicon.Bind("APIConfig", APIConfig);

        //Make users in api

    }

Here is the actual test I am trying to run

        [Fact]
        public async Task ApplyTest()
        {
            _client.SetBearerToken(await GetAccessToken());

            // Act
            var response = await _client.GetAsync($"{APIConfig.ApiBaseUrl}api/call/");
             response.EnsureSuccessStatusCode();
        }

And here is GetAccessToken, which returns the JWT token that the integration uses to identify itself as a user... At least that's the plan.

        protected async Task<string> GetAccessToken()
    {
        var disco = await _authClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
        {
            Address = _authServer.BaseAddress.ToString(),
            Policy =
                {
                    ValidateIssuerName = false
                }
        });

        if (!String.IsNullOrEmpty(disco.Error))
        {
            throw new Exception(disco.Error);
        }
        var response = await _authClient.RequestPasswordTokenAsync(new PasswordTokenRequest
        {
            Address = disco.TokenEndpoint,
            GrantType = "password",
            ClientId = "api",

            UserName = "[email protected]",
            Password = "test"
        });

        return response.AccessToken;
    }

The only identifiable difference I can find between a JWT token generated through this function and that of a normal website request is that the Public Key is missing (according to jwt.io).

Finally, because I think this bit might be useful, here's the part of the API's Startup.cs where I enforce the use of the JWTBackChannelHandler

if (Environment.EnvironmentName == "Test")
            {
                services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                    .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, o =>
                    {
                        o.JwtBackChannelHandler = _identityServerMessageHandler;
                        o.RequireHttpsMetadata = false;
                    })
                    .AddApiKeyAuthenication(o =>
                    {
                        o.Keys.Add("****".ToSha256());
                        o.Keys.Add("****".ToSha256());
                    });
            }

Thank you very much in advance for your help, I've kind of reached the end of my wits with this one. Please let me know if you want any more information and I'll be happy to provide.

1

There are 1 best solutions below

0
On

After another 2 hours of digging I found out that I needed to add

o.Authority = identityServerOptions.Address.AbsoluteUri;

To the AddIdentityServerAuthentication.

Thank you to those who took the time into digging into my issue for me.