Skip to content

feat: RFC 8705 mutual-TLS client authentication for CF app instance identity#3972

Draft
rkoster wants to merge 20 commits into
cloudfoundry:developfrom
rkoster:feat/rfc8705-mtls-client-auth
Draft

feat: RFC 8705 mutual-TLS client authentication for CF app instance identity#3972
rkoster wants to merge 20 commits into
cloudfoundry:developfrom
rkoster:feat/rfc8705-mtls-client-auth

Conversation

@rkoster

@rkoster rkoster commented Jul 3, 2026

Copy link
Copy Markdown

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 cert
for a UAA JWT containing verified app_guid, space_guid, org_guid, and
cf_instance_guid claims — without secrets or user credentials.

How it works

CF app ──cert──▶ Gorouter (sanitize_set) ──XFCC──▶ UAA /oauth/mtls/token
                                                         │
                                            ClientCertificateMapper
                                            (XFCC → X509Certificate attr)
                                                         │
                                            ClientDetailsAuthenticationProvider
                                            (tls_client_auth: PKIX chain validation)
                                                         │
                                            MtlsClaimsEnhancer
                                            (cert OU → app/space/org_guid claims)
                                                         │
                                            ◀── JWT with CF identity claims ──

Changes (this PR — 18 commits)

Model layer:

  • ClientAuthentication: add tls_client_auth constant
  • TokenConstants: add CLIENT_AUTH_TLS_CLIENT_AUTH
  • TlsClientAuthConfiguration: per-client CA PEM + claim-mapping model
  • UaaClientDetails: add tlsClientAuthConfiguration field
  • OpenIdConfiguration: add mtls_endpoint_aliases to OIDC discovery

Server layer:

  • ClientDetailsAuthenticationProvider: isTlsClientAuth(), validateTlsClientAuth(), getTlsClientAuthConfiguration() — handles in-memory, Map (Jackson), and flat String PEM (BOSH) config forms
  • TlsClientAuthentication: PKIX cert chain validation against per-client CA
  • ClientCertificateMapper registration: SpringServletXmlFiltersConfiguration registers the java-buildpack-client-certificate-mapper-jakarta filter for /oauth/mtls/* to materialise X-Forwarded-Client-Cert as a jakarta.servlet.request.X509Certificate attribute
  • ClientCredentialsTokenGranter: allow tls_client_auth alongside client_secret
  • MtlsClaimsEnhancer: UaaTokenEnhancer that reads cert subject OU fields and maps them to JWT claims per per-client configuration; handles DB-loaded clients (reads additionalInformation directly) and Diego multi-valued RDNs
  • FilterChainOrder.OAUTH_11 + mtlsTokenEndpointSecurity: dedicated security filter chain for /oauth/mtls/token with CSRF disabled
  • UaaTokenEndpoint: add /oauth/mtls/token to @RequestMapping
  • OIDC discovery: expose mtls_endpoint_aliases

Deployment notes

Requires the Gorouter to be configured with forwarded_client_cert: sanitize_set
so it validates the TLS session cert and injects it as X-Forwarded-Client-Cert.

The UAA client for an app must be configured with:

token-endpoint-auth-method: tls_client_auth
tls-client-auth-ca: <instance-identity CA certificate PEM>
tls-client-auth-claim-mappings:
  - field: subject_cn
    claim: cf_instance_guid
  - field: subject_ou
    pattern: "app:(.+)"
    claim: app_guid
  - field: subject_ou
    pattern: "space:(.+)"
    claim: space_guid
  - field: subject_ou
    pattern: "organization:(.+)"
    claim: org_guid

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, and cf space --guid.

Related

rkoster added 18 commits July 3, 2026 10:33
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().
…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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/token with a dedicated Spring Security filter chain and request-to-certificate mapping via ClientCertificateMapper.
  • Adds TLS client certificate validation (TlsClientAuthentication) and a token enhancer (MtlsClaimsEnhancer) to emit cnf.x5t#S256 plus 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.

Comment thread server/build.gradle.kts Outdated
rkoster added 2 commits July 6, 2026 09:45
- 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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

3 participants