Keycloak Testcontainers not processing Refit request with Body

48 views Asked by At

I am using the Keycloak Testcontainers library to run integration tests on my Net Core 7 project. I am also using the refit library to handle the api communication with the Keycloak Testcontainer.

Now all the api calls which don't have a body (getting realm settings, tokens etc) work well. The problem is when I try and send a request with a body such as when creating a new user and keycloak is returning a bad request error.

Refit is sending the request and when I am debugging I noticed that the content is being sent as pushatreamcontent and not a serialised json body.

Method: POST, RequestUri: 'http://localhost:53561/admin/realms/mNet/users', Version: 1.1, Content: System.Net.Http.PushStreamContent, Headers: { Content-Type: application/json; charset=utf-8 }

Any ideas?

Integration test

namespace mNet.Identity.IntegrationTests;

[Collection("Keycloak Test Collection")]
public class UserRegistrationSpecification
{
    private readonly IKeycloakUserAdministrationService _sut;

    private readonly IDateTimeProvider _dateTimeProvider = 
        Substitute.For<IDateTimeProvider>();
    private readonly ILoggerAdapter<KeycloakUserAdministrationService> _logger = 
        Substitute.For<ILoggerAdapter<KeycloakUserAdministrationService>>();
    private readonly ILoggerAdapter<AdminAuthorizationDelegatingHandler> _delegatingHandlerLogger = 
        Substitute.For<ILoggerAdapter<AdminAuthorizationDelegatingHandler>>();

    public UserRegistrationSpecification(KeycloakTestContainer keycloakTestContainer)
    {
        var keycloakService = keycloakTestContainer.KeycloakService;
        
        var keycloakTokenServiceRefitApi = RestService.For<IKeycloakTokenServiceRefitApi>(
            hostUrl: keycloakService.TokenServiceUri!.AbsoluteUri);

        var adminAuthorizationDelegatingHandler = new AdminAuthorizationDelegatingHandler(
            keycloakService: keycloakService,
            keycloakTokenServiceRefitApi: keycloakTokenServiceRefitApi,
            logger: _delegatingHandlerLogger);

        var httpClient = new HttpClient(adminAuthorizationDelegatingHandler)
        {
            BaseAddress = keycloakService.AccessManagementUri
        };

        var keycloakAccessManagementRefitApi = RestService.For<IKeycloakAccessManagementRefitApi>(
            client: httpClient);
        
        _sut = new KeycloakUserAdministrationService(
            dateTimeProvider: _dateTimeProvider,
            keycloakService: keycloakService,
            keycloakAccessManagementRefitApi: keycloakAccessManagementRefitApi,
            logger: _logger);

        _dateTimeProvider.GetCurrentUtcTime().Returns(
            CommonTestConstants.DateTime.CurrentTime);
    }
    
    [Fact]
    public async Task Should_register_new_user_on_keycloak_when_valid_user_provided()
    {
        // Arrange
        var user = UserTestUtilities.CreateUser.WithEmailOnly();

        // Act
        var result = await _sut.RegisterUserAsync(
            user: user,
            password: UserTestConstants.Setup.Password);

        // Assert  
        result.IsSuccess.ShouldBeTrue();
    }
    
}

Register user method

public async Task<Result<KeycloakId>> RegisterUserAsync(User user, string password,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var userRepresentationModel = user.MapToUserRepresentationKeycloakModel(_dateTimeProvider);

            userRepresentationModel.Credentials = new CredentialRepresentation[]
            {
                new()
                {
                    Value = password,
                    Temporary = false,
                    Type = KeycloakConstants.CredentialTypes.Password
                }
            };

            var response = await _keycloakAccessManagementRefitApi.RegisterUserAsync(userRepresentationModel);

            response.EnsureSuccessStatusCode();
            
            _logger.LogDebug("Successfully registered user {UserId} through Keycloak API",
                user.Email);

            return ExtractKeycloakIdFromLocationHeader(response);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unable to register user {UserId} through Keycloak API",
                user.Email);
            return Result.Failure<KeycloakId>(KeycloakErrors.Api.RegisterUserFailure);
        }
    }

Authorisation handler (returns token successfully from Keycloak)

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        try
        {
            // Check client details set
            var getAdminClientTokenRequestParametersResult = _keycloakService.GetAdminClientTokenRequestParameters();
            
            if (getAdminClientTokenRequestParametersResult.IsFailure)
                throw new Exception(getAdminClientTokenRequestParametersResult.Error.Message);
            
            // Obtain the authorization token from Keycloak
            var authorizationToken = await 
                _keycloakTokenServiceRefitApi.GetClientAccessToken(
                    getAdminClientTokenRequestParametersResult.Value);
            
            _logger.LogDebug("Successfully obtained client access token from Keycloak");

            // Attach the token to the request
            request.Headers.Authorization = new AuthenticationHeaderValue(
                JwtBearerDefaults.AuthenticationScheme,
                authorizationToken.AccessToken);

            // Send the request and obtain the response
            var httpResponseMessage = await base.SendAsync(request, cancellationToken);

            httpResponseMessage.EnsureSuccessStatusCode();
            
            _logger.LogDebug("Successfully processed the following HTTP request on Keycloak API: {HttpRequest}",
                request.ToString());

            return httpResponseMessage;
        }
        catch (Exception ex)
        {
            // Log error and return bad request response upon exception
            _logger.LogError(ex, "Unable to process the following HTTP request on Keycloak API: {HttpRequest}",
                request.ToString());
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        }
    }

Refit api interfaces

[Post("/users")]
    Task<HttpResponseMessage> RegisterUserAsync([Body] UserRepresentation userRepresentationModel);
0

There are 0 answers