I am trying to add keycloak as a testcontainer to my .net core (5) integration tests using the dotnet-testcontainers library .
My Problem is, I am struggling with HTTPS-Support having a container using self-signed certificates and TestServer-Class for my integration tests.
To be precise, I am using Microsofts TestServer class to create real API requests with an in-memory config for using a keycloak-testcontainer with exposed port 8443 and its self-signed certificate.
The Problem is: I can’t add a HttpClientHandler
to TestServers HttpClient(created via serverCreateClient()
) to allow non-trusted certs within this handler. I have created a concrete example here on branch apitests-https. The failing test can be found here, its in the SucceedsWhenGetRequestWithTokenReturnsListOfArticles
test method. I added some Comments to the class and the Startup.cs of DemoApi - Project that shows what i've tried.
As a result, the TestServers internal Jwt Middleware uses the default HttpClient and throws the following AuthenticationException:
---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'System.String'.
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch, RemoteCertificateChainErrors
at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
I already tried multiple things to make it work which are commented in the code.
DemoApi/Startup.cs
: Tried to add my own "Testing" Environment with following Code:ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
using it via UseEnvironment("Testing")
when creating the TestServer-Instance in the Api Test. Debugging shows the code is called, but still the exception occurs.
DemoApiTestsApi/BaseFixture.cs
orDemoApiTests/Infrastructure/Persistence/KeycloakTest.cs
: See here for a working Implementation of my own HttpClient with handler to obtain the token in general (this works in the branch) -GetTestToken
is the method in BaseFixture.
So, honestly I am a bit out of ideas on how to make this work with TestServer or otherwise. Essentially, I need the handler I use in BaseFixture.GetTestToken()
/KeycloakTest.cs
to be also used within my TestServer instance, but can't apply it in the CreateClient()
which does not accept parameters. Any help is highly appreciated, may it be a solution or a hint to another way to solve this. TestServer is not necessarily fixed when there's another way to work this out.
Ok, I worked it out. Imho it is not the best solution, but as long as I don't have another one, this should work. All I had to do was:
IWebHostEnvironment
as a field to Startup.cs and inject in inStartup()
constructor.Startup then looks like this:
From here on, the rest was pretty easy. In my
ConfigureServices()
method, I can now check viaCurrentEnv.IsEnvironment("Testing")
if I am in an integration test, and for these I could bypass ssl validation by setting the jwt BackChannelHandler manually.Code:
I have to admit, this feels hacky and at best I would love to not need to touch any application related code to make this test work. Best solution would be to get the certificate from my keycloak testcontainer and add it automatically to the trusted certs of the TestServers Application instance. If anyone could give another answer / a hint on how to get along with this, I'd be happy to see it. But for now this is what works best.