Skip to content
DocsTry Aspire
DocsTry

Keycloak integration

🧪 Preview Keycloak logo

Keycloak is an open-source Identity and Access Management solution aimed at modern applications and services. The Keycloak integration enables you to connect to existing Keycloak instances or create new instances from Aspire with the quay.io/keycloak/keycloak container image.

The Keycloak hosting integration models the server as the KeycloakResource type. To access this type and APIs, add the 📦 Aspire.Hosting.Keycloak NuGet package in your AppHost project:

Aspire CLI — Add Aspire.Hosting.Keycloak package
aspire add keycloak

The Aspire CLI is interactive, be sure to select the appropriate search result when prompted:

Aspire CLI — Example output prompt
Select an integration to add:
> keycloak (Aspire.Hosting.Keycloak)
> Other results listed as selectable options...

In your AppHost, call AddKeycloak to add and return a Keycloak resource builder:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
const webfrontend = await builder.addProject("webfrontend", "../Web/Web.csproj");
await webfrontend.withExternalHttpEndpoints();
await webfrontend.withReference(keycloak);
await webfrontend.withReference(apiService);
await webfrontend.waitFor(apiService);
await builder.build().run();

When Aspire adds a container image to the AppHost, it creates a new Keycloak instance on your local machine. The Keycloak resource includes default credentials:

  • KEYCLOAK_ADMIN: A value of admin
  • KEYCLOAK_ADMIN_PASSWORD: Random password generated using the default password parameter

When the AppHost runs, the password is stored in the AppHost’s secret store in the Parameters section:

{
"Parameters:keycloak-password": "<THE_GENERATED_PASSWORD>"
}
  • Keycloak container imageQuay.io
    quay.io/keycloak/keycloak:26.6

    Added by AddKeycloak()addKeycloak().

    Source

Tags reflect the latest defaults on the microsoft/aspire main branch, and may be newer than the version pinned by the package you install.

To add a data volume to the Keycloak resource, call the WithDataVolume method on the Keycloak resource:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
await keycloak.withDataVolume();
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();

The data volume is used to persist the Keycloak data outside the lifecycle of its container. The data volume is mounted at the /opt/keycloak/data path in the Keycloak container.

Add Keycloak resource with data bind mount

Section titled “Add Keycloak resource with data bind mount”

To add a data bind mount to the Keycloak resource, call the WithDataBindMount method:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
await keycloak.withDataBindMount("/path/to/keycloak/data");
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();

When you want to explicitly provide the admin username and password used by the container image, you can provide these credentials as parameters:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const username = await builder.addParameter("username");
const password = await builder.addParameter("password", { secret: true });
const keycloak = await builder.addKeycloak("keycloak", {
port: 8080,
adminUsername: username,
adminPassword: password,
});
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();

For more information on providing parameters, see External parameters.

To import a realm into Keycloak, call the WithRealmImport method:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
await keycloak.withRealmImport("./Realms");
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();

The realm import files are copied to /opt/keycloak/data/import in the Keycloak container. Realm import files are JSON files that represent the realm configuration.

For production environments, consider these alternatives to seed your Keycloak instance:

Custom Keycloak image: Bake realm files into a custom image. Apply WithDockerfile to the Keycloak resource instead of replacing AddKeycloak with AddContainer:

keycloak/Dockerfile
FROM quay.io/keycloak/keycloak:25.0.0
COPY ./realms/*.json /opt/keycloak/data/import/
apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
await keycloak.withDockerfile("./keycloak");
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();
Directory layout
apphost.mts
keycloak/
Dockerfile
realms/
demo-realm.json

Initialization service: Create a separate initialization service or job that uses the Keycloak Admin REST API or Keycloak Admin Client to create and configure realms, clients, and users when the Keycloak instance first starts.

Infrastructure as Code: Use tools like Terraform with the Keycloak provider to manage realm configuration separately from your application deployment.

Keycloak containers can export telemetry to your OTLP collector using the WithOtlpExporter method:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const keycloak = await builder.addKeycloak("keycloak", { port: 8080 });
await keycloak.withOtlpExporter();
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
await apiService.withReference(keycloak);
await apiService.waitFor(keycloak);
await builder.build().run();

This enables Keycloak to send traces, metrics, and logs to the Aspire dashboard, providing better observability for your authentication flows.

Use PostgreSQL with the Community Toolkit extension

Section titled “Use PostgreSQL with the Community Toolkit extension”
⭐ Community Toolkit

The 📦 CommunityToolkit.Aspire.Hosting.Keycloak.Extensions package decorates the official KeycloakResource with WithPostgres / withPostgres. It configures Keycloak to use an official PostgresDatabaseResource, supplies the JDBC URL and credentials, and makes Keycloak wait for the database.

Aspire CLI — Add CommunityToolkit.Aspire.Hosting.Keycloak.Extensions package
aspire add communitytoolkit-keycloak-extensions

The Aspire CLI is interactive, be sure to select the appropriate search result when prompted:

Aspire CLI — Example output prompt
Select an integration to add:
> communitytoolkit-keycloak-extensions (CommunityToolkit.Aspire.Hosting.Keycloak.Extensions)
> Other results listed as selectable options...
apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const postgres = await builder.addPostgres('keycloak-postgres');
const database = await postgres.addDatabase('keycloakdb');
const keycloak = await builder.addKeycloak('keycloak', { port: 8080 });
await keycloak.withPostgres(database);
await builder.build().run();

By default, WithPostgres uses the PostgreSQL server’s username and password parameters. Its optional arguments let you supply different username and password parameter resources and set xaEnabled when Keycloak needs XA transactions.

The Keycloak hosting integration automatically adds an HTTP health check against the management endpoint’s /health/ready path.

To get started with the Keycloak client integration, install the 📦 Aspire.Keycloak.Authentication NuGet package in your ASP.NET Core project:

.NET CLI — Add Aspire.Keycloak.Authentication package
dotnet add package Aspire.Keycloak.Authentication

The Keycloak client integration registers JwtBearer and OpenId Connect authentication handlers in the DI container for connecting to a Keycloak server.

In the Program.cs file of your ASP.NET Core API project, call the AddKeycloakJwtBearer extension method to add JwtBearer authentication:

Program.cs
builder.Services.AddAuthentication()
.AddKeycloakJwtBearer(
serviceName: "keycloak",
realm: "api",
options =>
{
options.Audience = "store.api";
// For development only - disable HTTPS metadata validation
// In production, use explicit Authority configuration instead
if (builder.Environment.IsDevelopment())
{
options.RequireHttpsMetadata = false;
}
});

In the Program.cs file of your API-consuming project (for example, Blazor), call the AddKeycloakOpenIdConnect extension method to add OpenId Connect authentication:

Program.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddKeycloakOpenIdConnect(
serviceName: "keycloak",
realm: "api",
options =>
{
options.ClientId = "StoreWeb";
options.ResponseType = OpenIdConnectResponseType.Code;
options.Scope.Add("store:all");
});

When deploying Keycloak authentication to production environments, there are several important security considerations:

By default, JWT Bearer authentication in ASP.NET Core has RequireHttpsMetadata = true, which requires that the Authority URL uses HTTPS.

For production scenarios, configure the Authority URL explicitly:

Program.cs
builder.Services.AddAuthentication()
.AddKeycloakJwtBearer(
serviceName: "keycloak",
realm: "MyRealm",
configureOptions: options =>
{
// Explicitly set the Authority for production
if (!builder.Environment.IsDevelopment())
{
options.Authority = "https://your-keycloak-server.com/realms/MyRealm";
}
options.Audience = "my.api";
});

For production deployments, consider using connection strings:

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
,
function refExpr(strings: TemplateStringsArray, ...values: unknown[]): ReferenceExpression

Creates a reference expression from a tagged template literal

refExpr
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addConnectionString(name: string, options?: {
environmentVariableNameOrExpression?: ReferenceExpression;
}): IResourceWithConnectionString (+1 overload)

Adds a connection string resource

addConnectionString
('keycloak', {
environmentVariableNameOrExpression?: ReferenceExpression | undefined
environmentVariableNameOrExpression
:
function refExpr(strings: TemplateStringsArray, ...values: unknown[]): ReferenceExpression

Creates a reference expression from a tagged template literal

refExpr
`https://your-keycloak-server.com`,
});
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();
  • Always use RequireHttpsMetadata = true in production environments
  • Use secure, validated SSL certificates for your Keycloak server
  • Configure appropriate realm settings and client configurations in Keycloak
  • Implement proper token validation and audience checks
  • Consider using Keycloak’s built-in security features like rate limiting and brute force protection