-
Notifications
You must be signed in to change notification settings - Fork 531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add KeycloakRealmResource and AddRealm method #7120
base: main
Are you sure you want to change the base?
Conversation
No sample or tests? |
Added tests. What did you have in mind for examples? |
We have a keycloak playground project. Does it improve because of this new feature? |
No. The playground project is about putting applications in Keycloak, This is about putting Keycloak in applications. I'll add an example of how I'm using this next week, you'd like me to. But it requires nothing specific from Keycloak on the application side. Maybe this could be a generic component for using service discovery with identity. |
This commit introduces a new `AddRealm` method in the `KeycloakResourceBuilderExtensions` class, enabling the addition of Keycloak Realm resources to the application model. The method constructs a `KeycloakRealmResource` using an `IResourceBuilder<KeycloakResource>`, a realm name, and an optional realm name parameter. Additionally, a new `KeycloakRealmResource` class is defined, which includes properties for various Keycloak endpoints and their expressions, along with a constructor and detailed XML documentation. Changes to `PublicAPI.Unshipped.txt` reflect the inclusion of the new method and class in the public API.
The `KeycloakRealmResource` class has been refactored to include null checks in its constructor for parameters `name`, `realmName`, and `parent`. A private field `_parentEndpoint` and a property `ParentEndpoint` have been added. The `Parent` and `RealmName` properties now rely on the constructor for initialization. New unit tests in `KeycloakPublicApiTests.cs` ensure that the constructor throws an `ArgumentNullException` for null parameters. Additional tests validate that the `AddRealm` method correctly handles null values for the builder and realm name, improving input validation across the API.
cf0c19a
to
0d9d152
Compare
This is how I've been using this resource: var idp = builder.AddKeycloak(
name: "keycloak",
adminUsername: builder.AddParameter("KeycloakAdminUsername").ExcludeFromManifest(),
adminPassword: builder.AddParameter("KeycloakAdminPassword").ExcludeFromManifest())
.WithExternalHttpEndpoints()
...
.AddRealm("idp")
;
public static IResourceBuilder<TResource> WithServerAccessTokenHandler<TResource>(
this IResourceBuilder<TResource> builder,
IResourceBuilder<KeycloakRealmResource> idp)
where TResource : IResourceWithEnvironment, IResourceWithWaitSupport
{
return builder
.WaitFor(idp)
.WithEnvironment(ctx =>
{
var idpResource = idp.Resource;
ctx.EnvironmentVariables["JwtBearerOptions__Authority"] = idpResource.ConnectionStringExpression;
ctx.EnvironmentVariables["JwtBearerOptions__MetadataAddress"] = idpResource.MetadataAddressExpression;
ctx.EnvironmentVariables["JwtBearerOptions__Configuration__Issuer"] = idpResource.IssuerExpression;
ctx.EnvironmentVariables["JwtBearerOptions__Configuration__AuthorizationEndpoint"] = idpResource.AuthorizationEndpointExpression;
ctx.EnvironmentVariables["JwtBearerOptions__Configuration__TokenEndpoint"] = idpResource.TokenEndpointExpression;
ctx.EnvironmentVariables["JwtBearerOptions__Configuration__UserInfoEndpoint"] = idpResource.UserInfoEndpointExpression;
ctx.EnvironmentVariables["JwtBearerOptions__RequireHttpsMetadata"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__RequireSignedTokens"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__ValidateAudience"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__ValidateActor"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__ValidateIssuer"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__ValidateIssuerSigningKey"] = "false";
ctx.EnvironmentVariables["JwtBearerOptions__TokenValidationParameters__ValidateLifetime"] = "false";
})
;
}
public static IResourceBuilder<TResource> WithClientAccessTokenHandler<TResource>(
this IResourceBuilder<TResource> builder,
IResourceBuilder<KeycloakRealmResource> idp,
IResourceBuilder<ParameterResource> touchpointMediaProducerClientId,
IResourceBuilder<ParameterResource> touchpointMediaProducerClientSecret,
IResourceBuilder<ParameterResource> touchpointMediaProducerUsername,
IResourceBuilder<ParameterResource> touchpointMediaProducerPassword)
where TResource : IResourceWithEnvironment, IResourceWithWaitSupport
{
return builder
.WaitFor(idp)
.WithEnvironment("ClientCredentialsClient__ClientId", touchpointMediaProducerClientId)
.WithEnvironment("ClientCredentialsClient__ClientSecret", touchpointMediaProducerClientSecret)
.WithEnvironment("ClientCredentialsClient__Username", touchpointMediaProducerUsername)
.WithEnvironment("ClientCredentialsClient__Password", touchpointMediaProducerPassword)
.WithEnvironment("ClientCredentialsClient__TokenEndpoint", idp.Resource.TokenEndpointExpression)
;
}
``
On the server, I have:
```csharp
public static void AddAuthentication(this IServiceCollection services, IConfiguration configuration)
{
services.AddDistributedMemoryCache();
services
.AddOptions<AuthenticationOptions>()
.BindConfiguration("AuthenticationOptions")
.ValidateOnStart();
services.ConfigureOptions<JwtBearerBackchannelPostConfigureOptions>();
services
.AddOptions<JwtBearerOptions>("Bearer")
.BindConfiguration("JwtBearerOptions")
.ValidateOnStart();
services
.AddAuthentication()
.AddJwtBearer();
services.AddAuthorization(o => UserClaimsSetter.SetUserClaimsPolicy(o));
}
private sealed class JwtBearerBackchannelPostConfigureOptions : IPostConfigureOptions<JwtBearerOptions>
{
private readonly IHttpClientFactory _httpClientFactory;
public JwtBearerBackchannelPostConfigureOptions(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public void PostConfigure(string? name, JwtBearerOptions options)
{
options.Backchannel = string.IsNullOrEmpty(name) ? _httpClientFactory.CreateClient() : _httpClientFactory.CreateClient(name);
}
} On the client side, I have components to load IHttpClientFactory definitions and Nothing on theses services is directly dependent on the IDP being used on Aspire, that might or might not be the one used in production. |
Description
This PR introduces a new
AddRealm
method in theKeycloakResourceBuilderExtensions
class, enabling the addition of Keycloak Realm resources to the application model. The method constructs aKeycloakRealmResource
using anIResourceBuilder<KeycloakResource>
, a realm name, and an optional realm name parameter.Additionally, a new
KeycloakRealmResource
class is defined, which includes properties for various Keycloak endpoints and their expressions, along with a constructor and detailed XML documentation.Changes to
PublicAPI.Unshipped.txt
reflect the inclusion of the new method and class in the public API.Fixes #5092
Checklist
<remarks />
and<code />
elements on your triple slash comments?breaking-change
template):doc-idea
template):