EvSoftware.Infrastructure.Tests.Keycloak 0.1.0

dotnet add package EvSoftware.Infrastructure.Tests.Keycloak --version 0.1.0
                    
NuGet\Install-Package EvSoftware.Infrastructure.Tests.Keycloak -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EvSoftware.Infrastructure.Tests.Keycloak" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EvSoftware.Infrastructure.Tests.Keycloak" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="EvSoftware.Infrastructure.Tests.Keycloak" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EvSoftware.Infrastructure.Tests.Keycloak --version 0.1.0
                    
#r "nuget: EvSoftware.Infrastructure.Tests.Keycloak, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EvSoftware.Infrastructure.Tests.Keycloak@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EvSoftware.Infrastructure.Tests.Keycloak&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=EvSoftware.Infrastructure.Tests.Keycloak&version=0.1.0
                    
Install as a Cake Tool

EvSoftware.Infrastructure.Tests.Keycloak

This library provides a helper class (KeycloakHelper) designed to simplify integration testing of .NET applications secured by Keycloak, particularly when using Testcontainers to manage the Keycloak instance.

It allows you to programmatically interact with the Keycloak Admin REST API within your test setup (e.g., xUnit fixtures) to:

  • Create realms
  • Create OIDC or SAML clients
  • Create users with credentials
  • Obtain access tokens for test users via password grant

This avoids the need for static realm import files and makes test setup more dynamic and maintainable directly within your C# test code.

Prerequisites

  • .NET 8.0 (or compatible)
  • Docker environment accessible by Testcontainers

Installation

dotnet add package EvSoftware.Infrastructure.Tests.Keycloak

Usage

Typically, you would use this helper within an xUnit test fixture (IAsyncLifetime) that manages a Keycloak Testcontainer.

1. Setup Keycloak Testcontainer Fixture:

using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using EvSoftware.Infrastructure.Tests.Keycloak;
using System.Threading.Tasks;
using Xunit;

// Example Fixture (like KeycloakFixture.cs in integration tests)
public class MyKeycloakTestFixture : IAsyncLifetime
{
    private readonly IContainer _keycloakContainer;
    public KeycloakHelper KeycloakHelper { get; private set; } = null!;
    public string KeycloakBaseUrl { get; private set; } = null!;

    private const string KeycloakImage = "quay.io/keycloak/keycloak:22.0.1"; // Or desired version
    private const string AdminUser = "admin";
    private const string AdminPassword = "admin";

    public MyKeycloakTestFixture()
    {
        _keycloakContainer = new ContainerBuilder()
            .WithImage(KeycloakImage)
            .WithPortBinding(8080, true) // Map to random available host port
            .WithEnvironment("KEYCLOAK_ADMIN", AdminUser)
            .WithEnvironment("KEYCLOAK_ADMIN_PASSWORD", AdminPassword)
            .WithCommand("start-dev") // Start in dev mode (HTTP)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(8080))
            .Build();
    }

    public async Task InitializeAsync()
    {
        await _keycloakContainer.StartAsync();
        KeycloakBaseUrl = $"http://{_keycloakContainer.Hostname}:{_keycloakContainer.GetMappedPublicPort(8080)}";
        KeycloakHelper = new KeycloakHelper(KeycloakBaseUrl, AdminUser, AdminPassword);
    }

    public async Task DisposeAsync()
    {
        KeycloakHelper?.Dispose();
        if (_keycloakContainer != null)
        {
            await _keycloakContainer.StopAsync();
            await _keycloakContainer.DisposeAsync();
        }
    }
}

// Define a collection fixture
[CollectionDefinition("MyKeycloakTests")]
public class KeycloakCollection : ICollectionFixture<MyKeycloakTestFixture> { }

2. Use Helper in Tests:

Inject the fixture into your test class and use the KeycloakHelper instance.

using EvSoftware.Infrastructure.Tests.Keycloak.Representations;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;

[Collection("MyKeycloakTests")]
public class MyApiIntegrationTests
{
    private readonly MyKeycloakTestFixture _fixture;
    private readonly KeycloakHelper _keycloakHelper;
    private readonly HttpClient _apiClient; // Your API client

    public MyApiIntegrationTests(MyKeycloakTestFixture fixture)
    {
        _fixture = fixture;
        _keycloakHelper = fixture.KeycloakHelper;
        _apiClient = new HttpClient { BaseAddress = new Uri("http://localhost:5000") }; // Example API URL
    }

    [Fact]
    public async Task ProtectedEndpoint_ShouldReturnOk_WithValidToken()
    {
        // Arrange: Set up Keycloak using the helper
        var realmName = "test-realm";
        var clientId = "test-api-client";
        var username = "test-user";
        var password = "password123";

        await _keycloakHelper.CreateRealmAsync(new RealmRepresentation { Realm = realmName, Enabled = true });
        await _keycloakHelper.CreateClientAsync(realmName, new ClientRepresentation
        {
            ClientId = clientId,
            PublicClient = true, // Assuming public client for simplicity
            DirectAccessGrantsEnabled = true // Needed for password grant
        });
        await _keycloakHelper.CreateUserAsync(realmName, new UserRepresentation
        {
            Username = username,
            Credentials = new[] { new CredentialRepresentation { Type = "password", Value = password } }
        });

        // Get token
        string accessToken = await _keycloakHelper.GetAccessTokenAsync(realmName, clientId, username, password);

        // Act: Call protected API endpoint
        _apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var response = await _apiClient.GetAsync("/api/protected");

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }
}

Included DTOs

The library includes basic Data Transfer Object (DTO) classes under the EvSoftware.Infrastructure.Tests.Keycloak.Representations namespace to represent Keycloak entities:

  • RealmRepresentation
  • ClientRepresentation
  • UserRepresentation
  • CredentialRepresentation

These can be used when calling the KeycloakHelper methods.

Contributing

Contributions are welcome! Please feel free to open an issue or submit a pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details (assuming you add one).

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 165 4/5/2025