feat: RFC 8705 mutual-TLS client authentication for CF app instance identity#3972
Draft
rkoster wants to merge 20 commits into
Draft
feat: RFC 8705 mutual-TLS client authentication for CF app instance identity#3972rkoster wants to merge 20 commits into
rkoster wants to merge 20 commits into
Conversation
Add TlsClientAuthConfiguration field serialized as tls-client-auth-ca in client JSON, following the clientJwtConfig pattern. Includes getter/setter, copy constructor support, equals/hashCode. Fix fragile isPositive() hash code assertion to isNotZero().
…tailsAuthenticationProvider
…dpoint The BOSH ERB template emits 'mtls.endpoint' (from the nested mtls.endpoint YAML block) but the @value annotation was reading 'uaa.mtls_endpoint_path', a key never emitted by the template. Align the annotation to the actual Spring property so operator-configured paths are honoured.
…filter chain Add FilterChainOrder.OAUTH_11 (211) so the mTLS security chain can be ordered before the OAUTH_10 catch-all token endpoint chain. Add mtlsTokenEndpointSecurity @order(OAUTH_11) that: - matches /oauth/mtls/token - runs client-credentials authentication with tls_client_auth support - disables CSRF (stateless machine-to-machine endpoint) - uses BasicAuthenticationEntryPoint so Spring returns 401, not a redirect Without this chain the /oauth/mtls/token path falls through to the LoginSecurityConfiguration catch-all which rejects requests with CSRF-related 403 errors.
… expose /oauth/mtls/token getTlsClientAuthConfiguration previously handled only TlsClientAuthConfiguration (in-memory) and Map (Jackson-deserialized BOSH config stored as nested object). BOSH flat config stores tls-client-auth-ca as a plain PEM string and tls-client-auth-claim-mappings as a JSON array string — add an 'instanceof String pem' branch that parses both. UaaTokenEndpoint @RequestMapping previously covered only /oauth/token. After Gorouter sanitize_set was set, client authentication started succeeding for /oauth/mtls/token but Spring MVC returned 404 because no controller was mapped to that path. Add /oauth/mtls/token to the value array so the same endpoint handles both paths.
…ulti-valued RDNs Two bugs prevented CF identity claims from appearing in tokens issued via /oauth/mtls/token: 1. DB-loaded clients: UaaClientDetails.getTlsClientAuthConfiguration() returns null for clients loaded via JDBC because the tlsClientAuthConfiguration field is only set through JSON deserialization, not through the JDBC row-mapper path (which populates additionalInformation instead). Switch to loadTlsConfig() which mirrors ClientDetailsAuthenticationProvider.getTlsClientAuthConfiguration and reads from additionalInformation directly, handling the TlsClientAuthConfiguration/Map/String cases. 2. Multi-valued RDNs in Diego instance-identity certs: Diego encodes all three OU attributes (app:, space:, organization:) as a single multi-valued RDN using '+' as separator (RFC 2253 §2.2). Splitting only on ',' left the entire '+OU=space:..+OU=org:..' string attached to the app GUID. Fix extractOus() and extractRdnValue() to split on '+' within each RDN component.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds RFC 8705 mutual-TLS client authentication support for Cloud Foundry app instance identity by introducing a dedicated /oauth/mtls/token endpoint, validating instance certificates against per-client CA configuration, and enriching issued JWTs with CF identity claims derived from certificate subject fields. It also updates OIDC discovery to advertise mTLS endpoint aliases and tls_client_auth as a supported token endpoint authentication method.
Changes:
- Introduces
/oauth/mtls/tokenwith a dedicated Spring Security filter chain and request-to-certificate mapping viaClientCertificateMapper. - Adds TLS client certificate validation (
TlsClientAuthentication) and a token enhancer (MtlsClaimsEnhancer) to emitcnf.x5t#S256plus configured subject-derived claims. - Extends client auth method support across model/constants and OIDC discovery metadata (
tls_client_auth,mtls_endpoint_aliases).
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| server/src/test/java/org/cloudfoundry/identity/uaa/oauth/tls/TlsClientAuthenticationTest.java | Adds unit coverage for null inputs and malformed CA handling in TLS cert validation. |
| server/src/test/java/org/cloudfoundry/identity/uaa/oauth/tls/MtlsClaimsEnhancerTest.java | Verifies OU/CN claim extraction and cnf.x5t#S256 behavior for mTLS tokens. |
| server/src/test/java/org/cloudfoundry/identity/uaa/oauth/tls/ClientCertificateMapperFilterTest.java | Confirms servlet filter registration for mapping XFCC to request X509Certificate attribute on /oauth/mtls/*. |
| server/src/test/java/org/cloudfoundry/identity/uaa/oauth/provider/client/ClientCredentialsTokenGranterTests.java | Ensures tls_client_auth is allowed for client_credentials. |
| server/src/test/java/org/cloudfoundry/identity/uaa/authentication/UaaClientAuthenticationProviderTest.java | Updates provider wiring to include TlsClientAuthentication. |
| server/src/test/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProviderTests.java | Adds tests for mtls path detection and TLS config deserialization behavior. |
| server/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConnectEndpointsTest.java | Validates discovery document includes mtls_endpoint_aliases.token_endpoint. |
| server/src/main/java/org/cloudfoundry/identity/uaa/web/FilterChainOrder.java | Adds a new security chain order slot (OAUTH_11) for the mTLS token endpoint chain. |
| server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlFiltersConfiguration.java | Registers ClientCertificateMapper filter for /oauth/mtls/*. |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/UaaTokenEndpoint.java | Expands token endpoint mapping to include /oauth/mtls/token. |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/tls/TlsClientAuthentication.java | Adds per-client CA-based PKIX validation and request certificate extraction helper. |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/tls/MtlsClaimsEnhancer.java | Implements JWT enrichment from cert subject + cnf.x5t#S256 for the mTLS flow. |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/provider/client/ClientCredentialsTokenGranter.java | Allows tls_client_auth as a valid auth method for client credentials. |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/beans/OauthEndpointSecurityConfiguration.java | Adds dedicated security filter chain for /oauth/mtls/token (stateless + CSRF disabled). |
| server/src/main/java/org/cloudfoundry/identity/uaa/oauth/beans/OauthEndpointBeanConfiguration.java | Wires TlsClientAuthentication into ClientDetailsAuthenticationProvider bean construction. |
| server/src/main/java/org/cloudfoundry/identity/uaa/authentication/ClientDetailsAuthenticationProvider.java | Detects mTLS path, validates certs, and parses per-client TLS configuration from additional info. |
| server/src/main/java/org/cloudfoundry/identity/uaa/account/OpenIdConnectEndpoints.java | Populates mtls_endpoint_aliases in OIDC discovery. |
| server/build.gradle.kts | Adds dependency on the Gorouter client certificate mapper (Jakarta). |
| model/src/test/resources/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.json | Updates fixture to include tls_client_auth in supported auth methods. |
| model/src/test/java/org/cloudfoundry/identity/uaa/constants/ClientAuthenticationTest.java | Adds tests for tls_client_auth support, secret requirements, and validity rules. |
| model/src/test/java/org/cloudfoundry/identity/uaa/client/UaaClientDetailsTest.java | Adds JSON round-trip test for TLS client auth config and adjusts hashCode assertion. |
| model/src/test/java/org/cloudfoundry/identity/uaa/client/TlsClientAuthConfigurationTest.java | Adds unit tests for TLS auth config JSON round-tripping and equality semantics. |
| model/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConfigurationTests.java | Updates supported auth methods expectations and adds tests for mTLS aliases field. |
| model/src/main/java/org/cloudfoundry/identity/uaa/oauth/token/TokenConstants.java | Exposes CLIENT_AUTH_TLS_CLIENT_AUTH constant. |
| model/src/main/java/org/cloudfoundry/identity/uaa/constants/ClientAuthentication.java | Adds TLS_CLIENT_AUTH constant and updates supported/valid method logic and calculation. |
| model/src/main/java/org/cloudfoundry/identity/uaa/client/UaaClientDetails.java | Introduces tlsClientAuthConfiguration field and includes it in equals/hashCode. |
| model/src/main/java/org/cloudfoundry/identity/uaa/client/TlsClientAuthConfiguration.java | Adds model for trusted CA PEM + claim mapping configuration. |
| model/src/main/java/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.java | Adds mtls_endpoint_aliases and includes tls_client_auth in supported methods. |
- UaaClientDetails.setTlsClientAuthConfiguration() now syncs to additionalInformation so JDBC-loaded clients see the typed value - MtlsClaimsEnhancer checks the typed getTlsClientAuthConfiguration() field first, falls back to loadTlsConfig(additionalInformation) - ClientDetailsAuthenticationProvider and MtlsClaimsEnhancer now handle rawMappings instanceof List<?> (Jackson parses JSON arrays natively from JDBC; not always a String) - Move java-buildpack-client-certificate-mapper-jakarta to Gradle version catalog (libs.versions.toml) - SpringServletXmlFiltersConfiguration: remove class-level @SuppressWarnings and raw FilterRegistrationBean; add comment explaining why reflection is required (ClientCertificateMapper is package-private)
…lient_auth - Add tls_client_auth to tokenAMR assertion in wellKnownEndpoint tests - Assert mtls_endpoint_aliases.token_endpoint in both MockMvc test variants (SUBDOMAIN and ZONE_PATH zone resolution modes) - Document mtls_endpoint_aliases.token_endpoint in REST Docs snippet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements RFC 8705 mutual-TLS client
authentication for Cloud Foundry app instance identity, enabling workload identity
federation with AWS, GCP, Azure, and any OIDC-aware service.
CF app instances already receive a short-lived X.509 certificate from the Diego
Cell (
instance.crt/instance.key). This change lets an app exchange that certfor a UAA JWT containing verified
app_guid,space_guid,org_guid, andcf_instance_guidclaims — without secrets or user credentials.How it works
Changes (this PR — 18 commits)
Model layer:
ClientAuthentication: addtls_client_authconstantTokenConstants: addCLIENT_AUTH_TLS_CLIENT_AUTHTlsClientAuthConfiguration: per-client CA PEM + claim-mapping modelUaaClientDetails: addtlsClientAuthConfigurationfieldOpenIdConfiguration: addmtls_endpoint_aliasesto OIDC discoveryServer layer:
ClientDetailsAuthenticationProvider:isTlsClientAuth(),validateTlsClientAuth(),getTlsClientAuthConfiguration()— handles in-memory, Map (Jackson), and flat String PEM (BOSH) config formsTlsClientAuthentication: PKIX cert chain validation against per-client CAClientCertificateMapperregistration:SpringServletXmlFiltersConfigurationregisters thejava-buildpack-client-certificate-mapper-jakartafilter for/oauth/mtls/*to materialiseX-Forwarded-Client-Certas ajakarta.servlet.request.X509CertificateattributeClientCredentialsTokenGranter: allowtls_client_authalongsideclient_secretMtlsClaimsEnhancer:UaaTokenEnhancerthat reads cert subject OU fields and maps them to JWT claims per per-client configuration; handles DB-loaded clients (readsadditionalInformationdirectly) and Diego multi-valued RDNsFilterChainOrder.OAUTH_11+mtlsTokenEndpointSecurity: dedicated security filter chain for/oauth/mtls/tokenwith CSRF disabledUaaTokenEndpoint: add/oauth/mtls/tokento@RequestMappingmtls_endpoint_aliasesDeployment notes
Requires the Gorouter to be configured with
forwarded_client_cert: sanitize_setso it validates the TLS session cert and injects it as
X-Forwarded-Client-Cert.The UAA client for an app must be configured with:
Proof of concept
End-to-end verified on a real CF deployment: a Go app pushes its Diego instance cert
to
POST /oauth/mtls/token, and the returned JWT contains:{ "app_guid": "b0bff1c2-a258-4060-981d-601f22e6bcf8", "space_guid": "02700fa7-8db7-4598-b015-9a5fc73d4656", "org_guid": "8deb6c47-8460-4501-8a86-246b774d97e4", "cf_instance_guid": "86bf36e4-af79-4d7a-6484-0d89", "client_auth_method": "tls_client_auth", "cnf": { "x5t#S256": "rk4P4d0DXNJDpZeOotKRUzmoaomqSqPQn8OzyKQhMuw" } }All GUIDs verified against
cf app,cf org, andcf space --guid.Related
cloudfoundry/uaa-release