Skip to content

Aspire + Web API

This samples contains Keycloak installation configured via configuration files.

Here is what it does:

  1. Starts a Keycloak Instance as part of Aspire Integration
  2. Imports realm and test users (test1:test, test2:test)

The Keycloak is already configured, all you need to do is to run sample and try to retrieve token via Swagger UI.

Run:

bash
dotnet run --project ./AppHost

Code

AppHost:

cs
var builder = DistributedApplication.CreateBuilder(args);

var pgUser = builder.AddParameter("pg-username", "postgres");
var pgPassword = builder.AddParameter("pg-password", "postgres", secret: true);

var postgres = builder
    .AddPostgres("postgres", pgUser, pgPassword)
    .WithDataVolume()
    .WithLifetime(ContainerLifetime.Persistent)
    .AddDatabase("keycloak-db", databaseName: "keycloak");

var keycloak = builder
    .AddKeycloakContainer("keycloak")
    .WithDataVolume()
    .WithHostname("http://localhost:8080/")
    .WithPostgresDatabase(postgres)
    .WithOtlpExporter()
    .WithImport("./KeycloakConfiguration/Test-realm.json")
    .WithImport("./KeycloakConfiguration/Test-users-0.json");

var realm = keycloak.AddRealm("Test");

builder.AddProject<Projects.Api>("api").WithReference(keycloak).WithReference(realm);

builder.Build().Run();

Api:

cs
using System.Security.Claims;
using Keycloak.AuthServices.Authentication;
using Keycloak.AuthServices.Common;
using Microsoft.OpenApi;
using NSwag.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;

builder.AddServiceDefaults();

const string clientName = "workspaces-client";
const string schemeName = "oauth2";

var keycloakOptions = configuration.GetKeycloakOptions<KeycloakAuthenticationOptions>()!;
var realmUrl = keycloakOptions.KeycloakUrlRealm.TrimEnd('/') + "/";

services.AddOpenApi(options =>
{
    options.AddDocumentTransformer(
        (document, _, _) =>
        {
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes ??=
                new Dictionary<string, IOpenApiSecurityScheme>();
            document.Components.SecuritySchemes[schemeName] = new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.OAuth2,
                Flows = new OpenApiOAuthFlows
                {
                    AuthorizationCode = new OpenApiOAuthFlow
                    {
                        AuthorizationUrl = new Uri($"{realmUrl}protocol/openid-connect/auth"),
                        TokenUrl = new Uri($"{realmUrl}protocol/openid-connect/token"),
                        Scopes = new Dictionary<string, string>
                        {
                            ["openid"] = "OpenID",
                            ["profile"] = "Profile",
                        },
                    },
                },
            };

            document.Security =
            [
                new OpenApiSecurityRequirement
                {
                    [new OpenApiSecuritySchemeReference(schemeName, document)] = [],
                },
            ];

            return Task.CompletedTask;
        }
    );
});

services.AddKeycloakWebApiAuthentication(
    configuration,
    options =>
    {
        options.Audience = clientName;
        options.RequireHttpsMetadata = false;
    }
);
services.AddAuthorization();

var app = builder.Build();

app.MapOpenApi();
app.UseSwaggerUi(options =>
{
    options.DocumentPath = "/openapi/v1.json";
    options.Path = string.Empty;
    options.OAuth2Client = new OAuth2ClientSettings
    {
        ClientId = clientName,
        AppName = clientName,
        UsePkceWithAuthorizationCodeGrant = true,
        Scopes = { "openid", "profile" },
    };
});

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet(
        "/hello",
        (ClaimsPrincipal user) =>
            new
            {
                message = "Hello World!",
                name = user.Identity?.Name,
                authenticationType = user.Identity?.AuthenticationType,
                isAuthenticated = user.Identity?.IsAuthenticated ?? false,
                claims = user.Claims.Select(c => new
                {
                    c.Type,
                    c.Value,
                    c.Issuer,
                }),
            }
    )
    .RequireAuthorization();

app.Run();

See sample source code: keycloak-authorization-services-dotnet/tree/main/samples/GettingStartedAndAspire