Add Keycloak components to the Host app in the Keycloak example

This commit is contained in:
fiodarsazanavets
2025-12-01 19:24:49 +00:00
parent 2e4daa8074
commit 22d0f57780
4 changed files with 2257 additions and 4 deletions
@@ -1,12 +1,49 @@
using Microsoft.Extensions.Hosting;
using OnlineShop.AppHost.Extensions;
var builder = DistributedApplication.CreateBuilder(args);
var apiService = builder.AddProject<Projects.OnlineShop_ApiService>("apiservice")
.WithHttpHealthCheck("/health");
var idp = builder.AddKeycloakContainer(
"idp", tag: "23.0")
.ImportRealms("Keycloak")
.WithExternalHttpEndpoints();
builder.AddProject<Projects.OnlineShop_Web>("webfrontend")
var apiService = builder.AddProject<Projects.OnlineShop_ApiService>("apiservice")
.WithHttpHealthCheck("/health")
.WithReference(idp)
.WaitFor(idp);
var webFrontend = builder
.AddProject<Projects.OnlineShop_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health")
.WithReference(apiService)
.WaitFor(apiService);
.WithReference(idp, env: "Identity__ClientSecret")
.WaitFor(idp);
if (builder.Environment.IsDevelopment())
{
var webAppHttp = webFrontend.GetEndpoint("http");
var webAppHttps = webFrontend.GetEndpoint("https");
idp.WithEnvironment("WEBAPP_HTTP", () =>
$"{webAppHttp.Scheme}://{webAppHttp.Host}:{webAppHttp.Port}");
if (webAppHttps.Exists)
{
idp.WithEnvironment("WEBAPP_HTTP_CONTAINERHOST",
webAppHttps);
idp.WithEnvironment("WEBAPP_HTTPS", () =>
$"{webAppHttps.Scheme}://{webAppHttps.Host}:{webAppHttps.Port}");
}
else
{
idp.WithEnvironment("WEBAPP_HTTP_CONTAINERHOST",
webAppHttp);
}
}
builder.Build().Run();
@@ -0,0 +1,86 @@
namespace OnlineShop.AppHost.Extensions;
internal static class KeycloakHostingExtensions
{
public static IResourceBuilder<TResource> WithReference<TResource>(
this IResourceBuilder<TResource> builder,
IResourceBuilder<KeycloakResource> keycloakBuilder,
string env) where TResource : IResourceWithEnvironment
{
builder.WithReference(keycloakBuilder);
builder.WithEnvironment(
env, keycloakBuilder.Resource.ClientSecret);
return builder;
}
public static IResourceBuilder<KeycloakResource> AddKeycloakContainer(
this IDistributedApplicationBuilder builder,
string name,
int? port = null,
string? tag = null)
{
var keycloakContainer = new KeycloakResource(name)
{
ClientSecret = "some_secret"
};
var keycloak = builder.AddResource(keycloakContainer)
.WithAnnotation(new ContainerImageAnnotation
{
Registry = "quay.io",
Image = "keycloak/keycloak",
Tag = tag ?? "latest"
})
.WithHttpEndpoint(port: port, targetPort: 8080)
.WithEnvironment("KEYCLOAK_ADMIN", "admin")
.WithEnvironment("KEYCLOAK_ADMIN_PASSWORD", "admin")
.WithEnvironment("WEBAPP_CLIENT_SECRET", keycloakContainer.ClientSecret);
if (builder.ExecutionContext.IsRunMode)
{
keycloak.WithArgs("start-dev");
}
else
{
keycloak.WithArgs("start");
}
return keycloak;
}
public static IResourceBuilder<KeycloakResource>
ImportRealms(this IResourceBuilder<KeycloakResource>
builder, string source)
{
builder
.WithBindMount(source,
"/opt/keycloak/data/import")
.WithAnnotation(
new CommandLineArgsCallbackAnnotation(
args =>
{
args.Clear();
if (builder.ApplicationBuilder
.ExecutionContext.IsRunMode)
{
args.Add("start-dev");
}
else
{
args.Add("start");
}
args.Add("--import-realm");
}));
return builder;
}
}
internal class KeycloakResource(string name) :
ContainerResource(name),
IResourceWithServiceDiscovery
{
public string? ClientSecret { get; set; }
}
File diff suppressed because it is too large Load Diff
@@ -124,4 +124,13 @@ public static class Extensions
return app;
}
public static Uri GetIdpAuthorityUri(this HttpClient httpClient)
{
var idpBaseUri = httpClient.BaseAddress
?? throw new InvalidOperationException(
$"HttpClient instance does not have a BaseAddress configured.");
return new Uri(idpBaseUri, "realms/OnlineShop/");
}
}