Feature/38310 tls client auth idp brokering - #51414
Conversation
There was a problem hiding this comment.
Pull request overview
Adds RFC 8705 tls_client_auth support for OIDC identity-provider brokering, allowing realm key-provider certificates to authenticate outbound broker requests.
Changes:
- Adds per-IdP certificate resolution, mTLS SSL context creation, and certificate-authenticated backchannel requests.
- Extends the Admin UI, validation, documentation, and release notes with the new authentication method.
- Adds unit and integration coverage for configuration, certificate resolution, SSL context creation, and HTTP client lifecycle.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java |
Adds mTLS token, refresh, user-info, and introspection requests. |
services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java |
Uses the mTLS-capable client for logout and user-info calls. |
services/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java |
Adds certificate-provider configuration and validation. |
services/src/main/java/org/keycloak/broker/oidc/mtls/IdpClientCertificateResolver.java |
Resolves enabled realm certificate keys. |
services/src/main/java/org/keycloak/broker/oidc/mtls/IdpMtlsSslContextProvider.java |
Builds an SSL context from realm key material. |
server-spi-private/src/main/java/org/keycloak/http/simple/SimpleHttp.java |
Adds owned-client completion behavior. |
server-spi-private/src/main/java/org/keycloak/http/simple/SimpleHttpRequest.java |
Closes dedicated clients after request completion. |
server-spi-private/src/main/java/org/keycloak/http/simple/SimpleHttpResponse.java |
Runs completion cleanup after consuming responses. |
js/apps/admin-ui/src/identity-providers/add/OIDCAuthentication.tsx |
Adds the mTLS authentication and certificate-provider controls. |
js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties |
Adds UI labels and help text. |
services/src/test/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfigTest.java |
Tests configuration detection and validation. |
services/src/test/java/org/keycloak/broker/oidc/mtls/IdpClientCertificateResolverTest.java |
Tests certificate-key resolution. |
services/src/test/java/org/keycloak/broker/oidc/mtls/IdpMtlsSslContextProviderTest.java |
Tests SSL context construction. |
server-spi-private/src/test/java/org/keycloak/http/simple/SimpleHttpOwnedClientTest.java |
Tests dedicated HTTP client cleanup. |
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/OidcTlsClientAuthBrokerConfigTest.java |
Tests Admin API configuration acceptance and rejection. |
docs/documentation/server_admin/topics/identity-broker/oidc.adoc |
Documents mTLS identity-provider configuration. |
docs/documentation/release_notes/topics/26_7_0.adoc |
Announces the new brokering capability. |
| @Override | ||
| @Test | ||
| public void testLogInAsUserInIDP() { | ||
| // Intentionally left blank — see class javadoc. No live handshake in the standard profile. |
There was a problem hiding this comment.
server-side certificate selection + SSLContext are unit-tested (IdpMtlsSslContextProviderTest, OAuth2IdentityProviderConfigTest); the live-handshake-test needs a new framework-infrastructure (a HttpsServer with setNeedClientAuth(true), filled from ManagedCertificates) and is documented as follow-up.
615371b to
c2c17d1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
services/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java:308
- This overload bypasses legacy
validate(RealmModel)overrides in subclasses. For example,GoogleIdentityProviderConfig's issuer check and the generic OAuth provider's required-claim checks no longer run now thatRepresentationToModelcalls only this overload; dispatch throughvalidate(realm)before the common checks (or migrate every existing override).
public void validate(KeycloakSession session, RealmModel realm) {
services/src/main/java/org/keycloak/connections/httpclient/DefaultHttpClientFactory.java:206
- With the supported
disable-trust-manager=truesetting,configureBuildersets the disable flag andHttpClientBuilder.build()replaces this supplied SSL context with a new context initialized with null key managers (HttpClientBuilder.java:270-275). The resulting mTLS client never presents the selected certificate, sotls_client_authfails; preserve the custom key managers when applying the passthrough trust manager.
if (sslContext != null) {
builder.sslContext(sslContext);
server-spi-private/src/main/java/org/keycloak/http/simple/SimpleHttpRequest.java:258
- The owned client is closed only when
client.executethrows, butcreateHttpRequest, form/entity creation, and header configuration can all throw before thistry(for example for an invalid URL or a POST with no content). Those failures leak the dedicated client; widen the cleanup guard to cover the entire request-construction path.
try {
return new SimpleHttpResponse(client.execute(httpRequest), maxConsumedResponseSize, objectMapper, this::onCloseRequest);
} catch (IOException | RuntimeException e) {
onCloseRequest();
throw e;
services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java:746
- This overload resets
max-consumed-response-sizeto the hard-coded 10 MB default, unlikeSimpleHttp.create(session), so every mTLS backchannel request ignores the operator's configured response-size limit. CarryHttpClientProvider.getMaxConsumedResponseSize()onto the owned client.
return SimpleHttp.create(buildMtlsHttpClient(), CLOSE_CLIENT);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
services/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java:309
- Calling the new overload here bypasses existing subclasses that override only
validate(RealmModel). In particular, the anonymous config inOAuth2IdentityProviderFactory.java:59-75will no longer reject missing user-info/claim settings, so invalid generic OAuth IdPs can now be persisted; invoke the legacy overload before the new session-dependent checks (or keep the shared checks in that overload).
public void validate(KeycloakSession session, RealmModel realm) {
SslRequired sslRequired = realm.getSslRequired();
services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java:746
- This overload initializes
SimpleHttpwithDEFAULT_MAX_CONSUMED_RESPONSE_SIZE, so mTLS responses ignore the provider's configuredmax-consumed-response-sizewhile non-mTLS requests honor it. Carry the provider limit onto the owned-client request as well.
return SimpleHttp.create(buildMtlsHttpClient(), CLOSE_CLIENT);
js/apps/admin-ui/src/identity-providers/add/OIDCAuthentication.tsx:53
- Swallowing the expected 403 leaves
certKeyProviderOptionsempty, so an administrator withmanage-identity-providersbut notview-realmcan selecttls_client_authbut cannot supply its required key provider and every save is rejected. Either expose the eligible providers through an endpoint authorized for IdP management or clearly require/report the additional permission instead of silently rendering an unusable form.
try {
const keysMetadata = await adminClient.realms.getKeys({ realm });
return keysMetadata.keys ?? [];
} catch (error) {
console.warn(
js/apps/admin-ui/src/identity-providers/add/OIDCAuthentication.tsx:68
- The keys endpoint includes
DISABLEDkeys, but this filter offers every entry with a certificate; selecting a disabled provider then deterministically fails the server-sideisEnabled()validation. Filter the options to the ACTIVE/PASSIVE statuses accepted byIdpClientCertificateResolver.
if (k.certificate && k.providerId && !seen.has(k.providerId)) {
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated no new comments.
Suppressed comments (3)
services/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java:309
- Changing this override to the session-aware overload bypasses existing
validate(RealmModel)overrides in subclasses. For example, the generic OAuth provider's required user-info/claim checks andGoogleIdentityProviderConfig's issuer validation no longer run during create/update; dispatch the legacy overload before the new session-dependent checks.
public void validate(KeycloakSession session, RealmModel realm) {
SslRequired sslRequired = realm.getSslRequired();
services/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java:742
- This
SimpleHttp.createoverload always usesDEFAULT_MAX_CONSUMED_RESPONSE_SIZE, so mTLS backchannel calls ignore the server's configured response-size limit thatSimpleHttp.create(session)honors. Carry the provider limit over to avoid changing the memory bound only fortls_client_authrequests.
return SimpleHttp.create(buildMtlsHttpClient(), CLOSE_CLIENT);
js/apps/admin-ui/src/identity-providers/add/OIDCAuthentication.tsx:68
- The selector includes
DISABLEDcertificate keys, whileIdpClientCertificateResolveronly acceptsACTIVEorPASSIVEkeys. Selecting such an offered provider therefore guarantees that create/update is rejected; filter options by enabled status.
if (k.certificate && k.providerId && !seen.has(k.providerId)) {
- Closes: keycloak#38310 Signed-off-by: Sebastian Pfahl <eryx@gmx.net>
Closes keycloak#51388 Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
Closes keycloak#50785 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
* Added default non-endpoint methods fixes: keycloak#50221 Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * changed types Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> --------- Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Updated RolePermissions.isRealmAdminRole() to explicitly check that the role belongs to the master realm before blocking it. This ensures that application realms can use and manage custom roles named admin or create-realm without being flagged as protected roles. Closes keycloak#51323 Signed-off-by: alehhu <159355663+alehhu@users.noreply.github.com>
Closes keycloak#51156 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
Signed-off-by: Sebastian Pfahl <eryx@gmx.net>
) Closes: keycloak#48216 Signed-off-by: Michal Vavřík <dev@michalvavrik.net>
…k#51309) * Close account console mobile nav after selecting a page If you have ever used Keycloak's Account Console on mobile, you probably noticed some UI/UX annoyances. For example, if you open the main menu, click on another category (e.g. Signing-in), the new page will be loaded in the brackground, but you sont immediately notice since the page is still overlayed by the main menu and you have to realize that you have to close it manually to continue. This patch fixes that issue. It appeared onn narrow viewports (phones, or a desktop browser window resized below PatternFly's 1200px breakpoint). Signed-off-by: Lars Kiesow <lkiesow@uos.de> * Add Playwright test for mobile nav auto-close This tests that on narrow viewports the sidebar nav is an overlay, and it must close after selecting a page instead of staying on top of the newly loaded content. A second test guards that desktop viewports keep the persistent sidebar open, so a future change can't silently drop the width check. Signed-off-by: Lars Kiesow <lkiesow@uos.de> --------- Signed-off-by: Lars Kiesow <lkiesow@uos.de>
Signed-off-by: Sebastian Pfahl <eryx@gmx.net>
129d98d to
1e4e8d8
Compare
Closes #38310