Skip to content

Conversation

max2020204
Copy link

@max2020204 max2020204 commented Aug 26, 2025

This PR introduces a new integration project that makes it easier to configure Keycloak to run with a Postgres database in Aspire-based applications.

Summary of changes

Added new project CommunityToolkit.Aspire.Keycloak.Postgres.

Implemented an extension method AddPostgres for IResourceBuilder to configure Keycloak with Postgres database resources:

Sets required environment variables (KC_DB, KC_DB_URL_*, KC_DB_USERNAME, KC_DB_PASSWORD, KC_TRANSACTION_XA_ENABLED).

Includes validation for missing or invalid Postgres server configuration.

Updated solution and package references to align with Aspire 9.4.1 dependencies.

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • New integration
    • Docs are written
    • Added description of major feature to project description for NuGet package (4000 total character limit, so don't push entire description over that)
  • Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs
  • Code follows all style conventions

Other information

Project name normalized to Postgres for correctness and consistency.

Extension method returns IResourceBuilder to allow chaining.

Uses proper IValueExpression instead of ToString() for connection strings.

Ensures JDBC-compatible environment variables for Keycloak.

- Introduced a new project `CommunityToolkit.Aspire.Keycloak.Postgress` for configuring Keycloak with PostgreSQL.
- Added an extension method to simplify Keycloak and Postgres database resource integration.
- Updated solution and package references for Aspire dependencies.
@max2020204 max2020204 changed the title **Add Keycloak with Postgres integration** Add Keycloak with Postgres integration Aug 26, 2025
@davidfowl
Copy link
Contributor

Did you test this?

- Enhanced parameter descriptions in XML documentation.
- Updated method to support asynchronous operations.
- Introduced additional environment variables for finer-grained database configuration.
- Added cancellation token support for improved task management.
@max2020204
Copy link
Author

max2020204 commented Aug 26, 2025

I have some problems, i can't access host, can you help me?

- Simplified XML documentation and parameter descriptions.
- Removed exceptions for parent resource validation.
- Replaced manual environment variable setup with connection string parsing using `NpgsqlConnectionStringBuilder`.
- Introduced optional port parameter for database configuration.
@max2020204
Copy link
Author

Now it working

@davidfowl
Copy link
Contributor

@@ -4,12 +4,13 @@
</PropertyGroup>
<ItemGroup Label="Aspire Packages">
<!-- Aspire packages -->
<PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting" Version="9.4.1" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is just done when you were testing, but ensure you roll that back and use the MSBuild variable

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had error
0>CommunityToolkit.Aspire.Keycloak.Postgres.csproj: Error NU1605 : Warning As Error: Detected package downgrade: Aspire.Hosting from 9.4.1 to 9.4.0. Reference the package directly from the project to select a different version.
CommunityToolkit.Aspire.Keycloak.Postgres -> Aspire.Hosting.Keycloak 9.4.1-preview.1.25408.4 -> Aspire.Hosting (>= 9.4.1)
CommunityToolkit.Aspire.Keycloak.Postgres -> Aspire.Hosting (>= 9.4.0)
0>------- Finished building project: CommunityToolkit.Aspire.Keycloak.Postgres. Succeeded: False. Errors: 1. Warnings: 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main branch is now updated to 9.4.1 Aspire.

@aaronpowell
Copy link
Member

Once there are tests and samples, we can get started on a review of the work.

@davidfowl
Copy link
Contributor

This implemenaton needs work:

  • You don't need to handle the event to make it work
  • This will only work when running but not when publishing.

See dotnet/aspire#8034 for more details.

Copy link
Member

@Alirexaa Alirexaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package name should be CommunityToolkit.Aspire.Keycloak.Extensions, like other extension packages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package name should be CommunityToolkit.Aspire.Keycloak.Extensions, like other extension packages.

Copy link
Author

@max2020204 max2020204 Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can i name it like CommunityToolkit.Aspire.Keycloak.Extensions.Postgres?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there may be other providers here.

max2020204 and others added 3 commits August 27, 2025 13:47
- Renamed `CommunityToolkit.Aspire.Keycloak.Postgress` to `CommunityToolkit.Aspire.Keycloak.Extensions.Postgres`.
- Refactored methods to improve flexibility, including support for different configurations (e.g., development, credentials).
- Introduced a dedicated test project `CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests`.
- Updated solution, dependencies, and project references accordingly.
@max2020204
Copy link
Author

I updated, add event as dev and add parameters for production

/// <returns>
/// The updated resource builder for the Keycloak resource.
/// </returns>
private static void WithPostgres(this IResourceBuilder<KeycloakResource> builder,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work with dev and production.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean void? After that, you can't add other methods, so I returned IResourceBuilder

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I mean that this approach using expressions works in both cases and you don’t need 2 implementations for run and publish

/// <returns>
/// The updated resource builder for the Keycloak resource.
/// </returns>
public static IResourceBuilder<KeycloakResource> WithPostgresDev(this IResourceBuilder<KeycloakResource> builder,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this overload at all.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You said that event will not work in prod, so I thought that this i will leave for the development environment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right so you don’t need this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be conditional logic to check for the different modes rather than having to call different methods.

$"jdbc:postgresql://{ep.Property(EndpointProperty.Host)}:" +
$"{ep.Property(EndpointProperty.Port)}/{dbName}");

builder.WithEnvironment("KC_DB", "postgres")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? Why is this hard coded?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if postgres is password protected (which is the default).

Copy link
Author

@max2020204 max2020204 Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

builder.WithEnvironment("KC_DB", "postgres")
You about this? Yeah, it's correct. There are other database vendors, but I'm writing for postgres
All environment variables: https://www.keycloak.org/server/all-config
I wanna make for other vendors such as dev-file (default), dev-mem, mariadb, mssql, mysql, oracle, postgres (all vendors that support)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if postgres is password protected (which is the default).

public static IResourceBuilder<KeycloakResource> WithPostgres(this IResourceBuilder<KeycloakResource> builder,
        IResourceBuilder<PostgresDatabaseResource> database, ParameterResource username, ParameterResource password)
    {
        ArgumentNullException.ThrowIfNull(username);
        ArgumentNullException.ThrowIfNull(password);
        WithPostgres(builder, database);
        builder.WithEnvironment("KC_DB_USERNAME", username)
            .WithEnvironment("KC_DB_PASSWORD", password);
        return builder;
    }

Here I'm setting by parameter that will set up from user, or what you mean?

Copy link
Member

@aaronpowell aaronpowell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hosting integration by the looks of it, so the project should reflect that.

Also, let's just make it Keycloak.Extensions so it's not PG specific, as that'll allow for other extensions on Keycloack to be included in here rather than going through a bunch of micro packages.

/// <returns>
/// The updated resource builder for the Keycloak resource.
/// </returns>
public static IResourceBuilder<KeycloakResource> WithPostgresDev(this IResourceBuilder<KeycloakResource> builder,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be conditional logic to check for the different modes rather than having to call different methods.

Comment on lines 3 to 8
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't needed as they are inherited. Check the other test projects and follow their patterns

Comment on lines 12 to 15
<PackageReference Include="coverlet.collector" Version="6.0.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="xunit" Version="2.9.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These will fail the build as CPM dictates versions

@@ -4,12 +4,13 @@
</PropertyGroup>
<ItemGroup Label="Aspire Packages">
<!-- Aspire packages -->
<PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting" Version="9.4.1" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main branch is now updated to 9.4.1 Aspire.

<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.Storage" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Dapr" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.Redis" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Keycloak" Version="9.4.1-preview.1.25408.4" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to introduce a MSBuild variable for the preview version of Aspire packages.

@@ -18,6 +19,7 @@
<PackageVersion Include="Aspire.Hosting.MongoDB" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.MySql" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="$(AspireVersion)" />
<PackageVersion Include="Moq" Version="4.20.72" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be under the Testing item group

@aaronpowell aaronpowell changed the title Add Keycloak with Postgres integration [WIP] Add Keycloak with Postgres integration Aug 28, 2025
@aaronpowell aaronpowell requested a review from Copilot August 28, 2025 00:50
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new Keycloak-PostgreSQL integration that enables configuring Keycloak resources to use PostgreSQL databases within Aspire applications. The integration provides extension methods to set up the necessary JDBC connection strings and environment variables required by Keycloak to connect to PostgreSQL databases.

  • Extension methods for integrating Keycloak with PostgreSQL databases
  • Support for both development and production scenarios with different credential handling approaches
  • Package reference updates to align with Aspire 9.4.1 dependencies

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres/KeycloakPostgresExtension.cs Core extension methods for Keycloak-PostgreSQL integration
src/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.csproj Project file with necessary Aspire package references
tests/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests/KeycloakExtensionPostgressTests.cs Empty test class placeholder
tests/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests.csproj Test project configuration
Directory.Packages.props Updated Aspire package versions and added new dependencies
CommunityToolkit.Aspire.slnx Solution file updated with new projects

Comment on lines 67 to 68
builder.WithEnvironment("KC_DB", "postgres")
.WithEnvironment("KC_DB", "postgres")
Copy link
Preview

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The environment variable 'KC_DB' is being set twice with the same value 'postgres'. Remove the duplicate line 68.

Suggested change
builder.WithEnvironment("KC_DB", "postgres")
.WithEnvironment("KC_DB", "postgres")

Copilot uses AI. Check for mistakes.

@max2020204
Copy link
Author

max2020204 commented Aug 28, 2025

 public static IResourceBuilder<KeycloakResource> WithPostgres(this IResourceBuilder<KeycloakResource> builder,
        IDistributedApplicationBuilder appBuilder, bool xaEnabled = false,
        string usernameParameter = "keycloak-username", string databaseName = "keycloak-db",
        string postgrsName = "keycloak-postgres")
    {
        var username = appBuilder.AddParameter(usernameParameter, "keycloak-db-user");
        var pwd = ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(appBuilder, "pg-pass");
        var password = appBuilder.CreateResourceBuilder(pwd);
        var keycloakPostgres = appBuilder.AddPostgres(postgrsName, username, password);
        var db = keycloakPostgres.AddDatabase(databaseName);

        builder.WithPostgres(db, username, password, xaEnabled)
            .WithReference(db)
            .WaitFor(keycloakPostgres);
        return builder;
    }

What you think about this method for easy dev environment?

Comment on lines 50 to 51
builder.WithEnvironment("KC_DB_USERNAME", username.Resource);
builder.WithEnvironment("KC_DB_PASSWORD", password.Resource);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you get these from the database.Parent instead? This overload is about using an existing postgres right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but i need to make task method with async/await. Will it be fine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need a task. Look at how the existing postgres integrations work in this project e.g.

public static IResourceBuilder<PostgresServerResource> WithDbGate(this IResourceBuilder<PostgresServerResource> builder, Action<IResourceBuilder<DbGateContainerResource>>? configureContainer = null, string? containerName = null)

You can see this dbgate extension resolving the username password and endpoints:

var userParameter = postgresServer.UserNameParameter is null
? ReferenceExpression.Create($"postgres")
: ReferenceExpression.Create($"{postgresServer.UserNameParameter}");
// DbGate assumes Postgres is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add($"LABEL_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"SERVER_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"USER_postgres{counter}", userParameter);
context.EnvironmentVariables.Add($"PASSWORD_postgres{counter}", postgresServer.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_postgres{counter}", postgresServer.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_postgres{counter}", "postgres@dbgate-plugin-postgres");

Since you care about this working when deployed as well, you don't need to change the endpoint resolution logic, but you can get the username and password from the postgres server.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, i see

max2020204 and others added 2 commits August 29, 2025 18:33
…ts/CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests.csproj

Co-authored-by: David Fowler <[email protected]>
…ibility

- Extracted `WithPostgresData` helper method to reduce redundant code.
- Improved default credential handling for Username and Password parameters.
- Updated XML documentation for clarity and consistency.
@max2020204 max2020204 requested a review from davidfowl August 29, 2025 16:09
- Removed `CommunityToolkit.Aspire.Keycloak.Extensions.Postgres.Tests`.
- Added `WithPostgres` tests to `CommunityToolkit.Aspire.Keycloak.Extensions.Tests`.
- Updated solution and project references to reflect restructuring.
@github-actions github-actions bot added the Stale label Sep 4, 2025
@aaronpowell aaronpowell removed the Stale label Sep 5, 2025
- Introduced detailed `README.md` explaining the Keycloak PostgreSQL Aspire extension's features, usage, and API.
- Added extensive unit tests in `KeycloakExtensionTests` for validating default credentials, explicit parameters, server parameters, and XA transaction enablement.
- Refactored `WithPostgres` method to streamline environment variable configuration and enhance maintainability.
@max2020204 max2020204 requested a review from Alirexaa September 7, 2025 21:29
@github-actions github-actions bot added the Stale label Sep 13, 2025
@github-actions github-actions bot closed this Sep 15, 2025
@davidfowl davidfowl reopened this Sep 15, 2025
@aaronpowell aaronpowell added awaiting response Waiting for the author of the issue to provide more information or answer a question and removed Stale labels Sep 15, 2025
Copy link
Member

@Alirexaa Alirexaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR needs an example (like other integrations) and integration tests.

…eferences

- Introduced `KeycloakWithPostgresIntegrationTest` for verifying Keycloak's behavior with PostgreSQL.
- Updated `WithPostgres` method to include a wait condition for database readiness.
- Added project reference to `CommunityToolkit.Aspire.Testing` for testing utilities.
@max2020204
Copy link
Author

This PR needs an example (like other integrations) and integration tests.

I added integration tests and there readme file with examples already exists

@davidfowl
Copy link
Contributor

@max2020204 I think @Alirexaa means a runnable sample in the playground, not just a readme.

@max2020204
Copy link
Author

@max2020204 I think @Alirexaa means a runnable sample in the playground, not just a readme.

Oh, Okey

- Added example projects under `examples/keycloak-postgres` for showcasing Keycloak integration with PostgreSQL in development and production environments.
- Introduced `AppHost` projects with service defaults for common functionalities like OpenTelemetry, resilience, and health checks.
- Updated `CommunityToolkit.Aspire.Keycloak.Extensions` with a description and additional package tags for better discoverability.
- Registered the new example projects in the solution file.
- Added conditional handling for `RequireHttpsMetadata` in development environments for both Dev and Prod projects.
- Updated `Aspire.Hosting` and `Aspire.Keycloak.Authentication` package versions to 9.4.2.
- Standardized XML formatting across example project files.
- Switched `Aspire.Hosting` and related package versions to reference `$(AspireVersion)` for consistency.
- Downgraded `Aspire.Keycloak.Authentication` from `9.4.2-preview.1.25428.12` to `9.4.1-preview.1.25408.4`.
@max2020204 max2020204 requested a review from Alirexaa September 16, 2025 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting response Waiting for the author of the issue to provide more information or answer a question
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants