Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
fe24edc
feat(model): add tls_client_auth constant and logic to ClientAuthenti…
rkoster Jul 3, 2026
91f18c9
feat(model): add CLIENT_AUTH_TLS_CLIENT_AUTH to TokenConstants
rkoster Jul 3, 2026
8d21fd4
feat(model): add TlsClientAuthConfiguration model class
rkoster Jul 3, 2026
7649861
feat(model): add tlsClientAuthConfiguration field to UaaClientDetails
rkoster Jul 3, 2026
95a01db
fix(model): add equals/hashCode to TlsClientAuthConfiguration and Cla…
rkoster Jul 3, 2026
5f18709
feat(model): add mtls_endpoint_aliases and tls_client_auth to OpenIdC…
rkoster Jul 3, 2026
95fc065
feat(server): add java-buildpack-client-certificate-mapper-jakarta de…
rkoster Jul 3, 2026
f883780
feat(server): register ClientCertificateMapper filter for /oauth/mtls/*
rkoster Jul 3, 2026
ca390e3
feat(server): add TlsClientAuthentication cert chain validation service
rkoster Jul 3, 2026
ee6201f
feat(server): add isTlsClientAuth / validateTlsClientAuth to ClientDe…
rkoster Jul 3, 2026
b0c3b20
fix(server): deserialize TlsClientAuthConfiguration from additionalIn…
rkoster Jul 3, 2026
5443718
feat(server): allow tls_client_auth in ClientCredentialsTokenGranter
rkoster Jul 3, 2026
446a8a3
feat(server): add MtlsClaimsEnhancer UaaTokenEnhancer for cert-derive…
rkoster Jul 3, 2026
fa226ea
feat(server): add mtls_endpoint_aliases to OIDC discovery document
rkoster Jul 3, 2026
40515e5
Fix @Value key in OpenIdConnectEndpoints to match ERB-emitted mtls.en…
rkoster Jul 3, 2026
3964568
fix(server): add OAUTH_11 order constant and dedicated mTLS security …
rkoster Jul 3, 2026
f954da6
fix(server): handle flat String PEM in getTlsClientAuthConfiguration;…
rkoster Jul 3, 2026
0ed40d4
fix(server): fix MtlsClaimsEnhancer for DB-loaded clients and Diego m…
rkoster Jul 3, 2026
08da0cd
fix(review): address PR feedback on mTLS client auth
rkoster Jul 6, 2026
e038d5d
test: update OIDC discovery tests for mtls_endpoint_aliases and tls_c…
rkoster Jul 6, 2026
c9be2f0
fix(review): fix JsonProperty collision and support full cert chain i…
rkoster Jul 6, 2026
901e4f8
test: update tlsClientAuthConfigRoundTripsViaJson for @JsonIgnore design
rkoster Jul 7, 2026
ad24fe6
feat: add subTemplate and audTemplates to TlsClientAuthConfiguration
rkoster Jul 7, 2026
04a3dfd
test: fix audTemplates equality isolation in TlsClientAuthConfigurati…
rkoster Jul 7, 2026
66d8173
refactor: restructure enhance() into phase 1/2 (vars + dot-notation)
rkoster Jul 7, 2026
14f4eca
fix: document single-level nesting, add conflict test, restore comment
rkoster Jul 7, 2026
16d366a
feat: add phase 3 sub/aud template rendering to MtlsClaimsEnhancer
rkoster Jul 7, 2026
70a03f6
fix: static PLACEHOLDER pattern, StringBuilder, restore comment, rena…
rkoster Jul 7, 2026
f4fcdb0
feat: read tls-client-auth-sub-template and aud-templates from flat B…
rkoster Jul 7, 2026
e94d338
fix: apply token enhancer overrides after UAA defaults so sub/aud tem…
rkoster Jul 7, 2026
046f03d
fix(review): isValidMethod accepts null method with CA-only config
rkoster Aug 18, 2026
553992d
fix(review): use LdapName for RFC 2253-safe DN parsing in MtlsClaimsE…
rkoster Aug 18, 2026
73483c5
test(review): confirm PKIX validation tolerates trust anchor in prese…
rkoster Aug 18, 2026
afc8801
fix(review): run ClientCertificateMapper filter before Spring Security
rkoster Aug 18, 2026
36f1f57
fix(review): require tls_client_auth before deriving mTLS claims
rkoster Aug 18, 2026
7b6af4f
feat: configure Tomcat connector to capture client certs without CA v…
rkoster Aug 19, 2026
3e500b2
fix(review): parameterize isCa in signCert instead of hardcoding true
rkoster Aug 19, 2026
2855b3a
feat: add per-client tls-client-auth-trusted-proxy-ca to TlsClientAut…
rkoster Aug 19, 2026
3abec45
feat: read tls-client-auth-trusted-proxy-ca in ClientDetailsAuthentic…
rkoster Aug 19, 2026
d182fa7
feat: read tls-client-auth-trusted-proxy-ca in MtlsClaimsEnhancer.loa…
rkoster Aug 19, 2026
90b506b
feat: capture genuine TLS peer certificate before ClientCertificateMa…
rkoster Aug 19, 2026
84125c1
feat: register RawPeerCertificateCaptureFilter before ClientCertifica…
rkoster Aug 19, 2026
e65f611
fix(review): add behavioral proof that captured attribute survives Cl…
rkoster Aug 19, 2026
1a19dbb
feat: validate raw TLS peer certificate against per-client trusted-pr…
rkoster Aug 19, 2026
f61ff3a
fix(review): log swallowed exceptions in isCertificateFromTrustedProxy
rkoster Aug 19, 2026
e0d5056
fix: resolve per-client TlsClientAuthConfiguration before fetching ce…
rkoster Aug 19, 2026
8a34536
fix(review): use hasCertificateFromRequest() as a cheap early exit in…
rkoster Aug 19, 2026
25e6a14
fix: resolve per-client TlsClientAuthConfiguration before fetching ce…
rkoster Aug 19, 2026
1ba3a09
test: add stringPathInAdditionalInformationLoadsTrustedProxyCa for Mt…
rkoster Aug 19, 2026
9a3c6fa
docs: clarify hasCertificateFromRequest javadoc covers raw TLS peer c…
rkoster Aug 19, 2026
db82bb5
feat: reject tls-client-auth-ca/tls-client-auth-trusted-proxy-ca when…
rkoster Aug 19, 2026
f14cfcf
fix: gate zone-endpoints client creation on uaa.mtls-enabled
rkoster Aug 19, 2026
c1b0326
fix: gate ClientAdminBootstrap YAML client config on uaa.mtls-enabled
rkoster Aug 19, 2026
febb328
fix(review): include clientId in checkMtlsClientConfigAllowed error m…
rkoster Aug 19, 2026
949c829
fix: disable TLSv1.3 on the mTLS connector to work around JSSE PHA li…
rkoster Aug 19, 2026
63b1df6
fix: advertise no acceptable-issuer constraint in the mTLS connector'…
rkoster Aug 19, 2026
28d2b82
docs: update RawPeerCertificateCaptureFilter javadoc to describe the …
rkoster Aug 19, 2026
aa51177
feat: register FIPS Bouncy Castle JSSE provider for the mTLS connector
rkoster Aug 20, 2026
1e0b698
feat: add BCJSSESSLContext, a BCJSSE-backed Tomcat SSLContext
rkoster Aug 20, 2026
15e8c50
test: cover the fail-fast path when BCJSSE is not registered
rkoster Aug 20, 2026
38febe6
feat: serve the mTLS connector's SSLContext from FIPS BCJSSE (TLS 1.3…
rkoster Aug 20, 2026
f687fa1
fix: derive BCJSSEUtil's implemented protocols/ciphers from BCJSSE, n…
rkoster Aug 20, 2026
b054b80
test: verify TLS 1.3 client-auth negotiation on the BCJSSE connector
rkoster Aug 20, 2026
7496ce8
test: remove clientSocketOfferingBothTls12And13, unused after the TLS…
rkoster Aug 20, 2026
9becf1c
fix(review): reject enhancer overrides of protected JWT claims
rkoster Aug 20, 2026
838bfc3
fix(review): run mtls filters for zone-path mTLS requests too
rkoster Aug 20, 2026
a3f1173
docs(review): document tls_client_auth and topology-dependent trusted…
rkoster Aug 20, 2026
78335c4
fix(review): scope tls-client-auth-trusted-proxy-ca to the proxy path…
rkoster Aug 20, 2026
aa07229
test(review): cover blank XFCC header and hasCertificateFromRequest i…
rkoster Aug 20, 2026
29103f0
docs(review): correct trusted-proxy-ca guidance to strict path separa…
rkoster Aug 20, 2026
d2175c0
Merge remote-tracking branch 'origin/develop' into feat/rfc8705-mtls-…
rkoster Aug 21, 2026
1163fc9
feat(review): add tls-client-auth-required-claims to TlsClientAuthCon…
rkoster Aug 21, 2026
ca7fc59
refactor(review): relocate claim-mapping extraction to TlsClientAuthe…
rkoster Aug 21, 2026
d84a180
feat(review): add certificateSatisfiesRequiredClaims to TlsClientAuth…
rkoster Aug 21, 2026
c565fe5
fix(review): enforce tls-client-auth-required-claims during tls_clien…
rkoster Aug 21, 2026
4a772df
docs(review): document tls-client-auth-required-claims
rkoster Aug 21, 2026
5b63fdd
fix(review): reject proxy-forwarded requests where ClientCertificateM…
rkoster Aug 21, 2026
1f39eaf
fix(review): fail closed on client-details lookup failure in MtlsClai…
rkoster Aug 21, 2026
5dbb65a
fix(review): enforce end-entity certificate constraints for tls_clien…
rkoster Aug 21, 2026
ed178dd
test(review): assert specific rejection messages in end-entity constr…
rkoster Aug 21, 2026
24dadff
fix(review): allow secretless mTLS clients via the zone client API
rkoster Aug 21, 2026
852b28b
fix(review): validate tls-client-auth-claim-mappings/templates/requir…
rkoster Aug 21, 2026
6f99d70
fix(review): reject null/blank tls-client-auth-required-claims values
rkoster Aug 21, 2026
aff499d
fix(review): use possessive quantifier in PLACEHOLDER regex to preven…
rkoster Aug 21, 2026
333a446
fix(review): fail token issuance instead of silently dropping cnf on …
rkoster Aug 21, 2026
c4d8aee
fix(review): bound template length to actually close the ReDoS gap
rkoster Aug 21, 2026
1aeffdf
fix(review): apply end-entity certificate constraints to the trusted-…
rkoster Aug 24, 2026
9883511
fix(review): fail fast if an existing BCJSSE provider isn't genuinely…
rkoster Aug 24, 2026
225e574
docs(review): document uaa.mtls-enabled in the configuration reference
rkoster Aug 24, 2026
0e78d80
fix(review): distinguish wrong-provider-class from wrong-FIPS-mode er…
rkoster Aug 24, 2026
a7b77e3
fix(review): only advertise tls_client_auth when mTLS is actually ena…
rkoster Aug 24, 2026
bf6149a
fix(review): only advertise the mtls_endpoint_aliases when mTLS is en…
rkoster Aug 24, 2026
3384e00
fix(review): fail fast if an existing BCFIPS provider isn't genuinely…
rkoster Aug 24, 2026
59b76c8
test(review): guard the tls_client_auth / mtls_endpoint_aliases coupl…
rkoster Aug 24, 2026
0070a1c
docs(review): clarify that the mTLS TLS-layer change is connector-wide
rkoster Aug 24, 2026
1babf25
docs(review): add REST Docs coverage for the /oauth/mtls/token endpoint
rkoster Aug 24, 2026
2390647
docs(review): wire the tls_client_auth REST Docs snippets into the re…
rkoster Aug 24, 2026
c1bbd85
fix(test): enable uaa.mtls-enabled for pre-existing discovery-documen…
rkoster Aug 24, 2026
b31e33f
fix(review): keep outbound IdP auth methods separate from inbound tls…
rkoster Aug 26, 2026
d0a0497
fix(review): reject tls client auth in code exchange
rkoster Aug 26, 2026
b13db7c
fix(review): derive inbound mTLS solely from tls-client-auth-ca and f…
rkoster Aug 26, 2026
36aa21f
test(review): make mTLS docs client secretless
rkoster Aug 26, 2026
cc5bf5d
fix(review): persist secretless mTLS docs client
rkoster Aug 26, 2026
67f7cf2
fix(review): clear persisted mTLS docs client secret
rkoster Aug 26, 2026
8ebd5eb
refactor(review): remove unused mTLS docs client binding
rkoster Aug 26, 2026
3b889d4
docs: document CA-only inbound mTLS selection
rkoster Aug 26, 2026
be41a4e
fix: reject blank CA for secretless zone clients
rkoster Aug 26, 2026
0a194a1
fix: narrow mTLS token endpoint boundary
rkoster Aug 26, 2026
e524955
docs: correct mTLS configuration references
rkoster Aug 26, 2026
d479e85
docs(review): render a TLS-capable mTLS curl and align client-auth se…
rkoster Aug 27, 2026
b8cc7bb
fix(review): normalize mTLS CA representations for zone clients
rkoster Aug 28, 2026
47d9d72
fix(review): handle null zone client additional information
rkoster Aug 28, 2026
8899760
fix(review): validate nested zone mTLS claim configuration
rkoster Aug 28, 2026
6967990
fix(review): validate nested zone mTLS configuration
rkoster Aug 28, 2026
eaafe83
fix(review): fail closed for nested mTLS configuration
rkoster Aug 28, 2026
b912c12
Merge remote-tracking branch 'origin/develop' into feat/rfc8705-mtls-…
rkoster Aug 28, 2026
34581fd
docs(review): align mTLS examples with JWT federation
rkoster Aug 28, 2026
7abdc26
docs(review): retain opaque auth code example
rkoster Aug 28, 2026
316de20
fix: extract all OU values from multi-valued RDNs
rkoster Aug 28, 2026
e902aef
docs(review): verify mTLS JWT certificate binding
rkoster Aug 31, 2026
86174f3
docs(review): render required mTLS client ID
rkoster Aug 31, 2026
d3a2bda
fix(review): validate mTLS properties without mappings
rkoster Aug 31, 2026
1e83ee7
fix(review): validate trusted proxy CA
rkoster Aug 31, 2026
bb516a8
test: remove invalid trusted proxy CA fixture
rkoster Aug 31, 2026
db63eeb
fix(review): validate configured client auth CA
rkoster Aug 31, 2026
78e348f
test: use valid client auth CA in bootstrap tests
rkoster Aug 31, 2026
ad7a4df
fix(review): gate mTLS endpoint and require flat client config
rkoster Sep 1, 2026
38a6a73
fix: remove nested mTLS client configuration
rkoster Sep 1, 2026
ca9398b
fix(review): require mTLS authentication for mTLS clients
rkoster Sep 1, 2026
2bc2d2a
fix(review): validate and clear mTLS client config
rkoster Sep 1, 2026
aeb4398
fix(review): preserve flat mTLS client config on copy
rkoster Sep 1, 2026
471ad57
test: reject expired mTLS client certificates
rkoster Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 133 additions & 8 deletions docs/UAA-Client-Authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ In [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749#section-2.3.1) the password
or better the process of checking its possession means the authentication process.

The secrets can be passed to a server in different ways. It can happen through the HTTP header and/or the body. In the case that an Authorization header is used,
the encoding of the secret needs to be done according to the RFC 6749. UAA fixed this behavior with https://github.com/cloudfoundry/uaa/issues/778.
the encoding of the secret needs to be done according to the RFC 6749. UAA fixed this behavior with <https://github.com/cloudfoundry/uaa/issues/778>.
The OIDC standard defines additional authentication mechanisms, see [section 9](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication).
The usage of secrets via client_secret_basic and client_secret_post is straightforward to set up and to use, however, if system-to-system communication is
in use, this can be a security problem because it will be hard to change secrets in running systems. The use of many secrets is not
Expand All @@ -17,10 +17,12 @@ standards define token-based authentication mechanisms for OAuth2 clients. They
* tls_client_auth [RFC 8705](https://www.rfc-editor.org/rfc/rfc8705)

## New methods

The new methods are based on asymmetric trust relation, so that the keys are divided into a private and a public one. The private key should never leave
the original system, but only the public key should be exchanged.

### private_key_jwt (Partly finished)

The standard private_key_jwt is similar to the existing JWT bearer flow, but JWT bearer is for user principle propagation, whereas private_key_jwt
is used for client authentication only. The used technics are similar and therefore the trust model is similar. Both usages are specified in the same
[RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.txt). The JWT bearer trust is based on parameters tokenKey and/or tokenKeyUrl parameter, part of the
Expand All @@ -29,7 +31,7 @@ of public keys, and this set can contain many keys because each key has its own
a dynamic token key URI. OIDC has defined the parameter jwks_uri for this already. The structure of the keys is defined with [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517).
UAA provides its own jwks_uri with endpoint /token_keys. The content of this endpoint is [JWKS](https://datatracker.ietf.org/doc/html/rfc7517#section-5).

The content of the JWT (parameter client_assertion) can be different. The standards define the difference. The [OIDC core standard](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
The content of the JWT (parameter client_assertion) can be different. The standards define the difference. The [OIDC core standard](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
simplifies the structure so that issuer and subject are the client_id of the authenticated OAuth2 client. The key rotation is supported with
jwks_uri, which retrieves the JWK. You can only have one JWKS_URI by the client. For the [RFC 7523 from OAuth2 standard](https://www.rfc-editor.org/info/rfc7523) the
structure is more complex, but with seperated issuer and subject there can be more than one entry of federated credential.
Expand All @@ -45,11 +47,131 @@ The new parameter for federated Credentials in UAA clients is (Work in progress

* jwt_creds

### tls_client_auth (Planned Feature)
Not yet defined a release date.
### tls_client_auth ([RFC 8705](https://www.rfc-editor.org/rfc/rfc8705))

Mutual-TLS client authentication: a client presents an X.509 certificate at the TLS layer
instead of a `client_secret` or a signed JWT assertion. UAA validates the certificate against
a per-client trusted CA and, optionally, derives JWT claims from the certificate's subject
fields (e.g. mapping a Cloud Foundry app instance identity certificate to `app_guid`/
`space_guid`/`org_guid` claims).

The client is authenticated on the fixed dedicated endpoint, `/oauth/mtls/token`, rather than
the regular `/oauth/token`. A nonblank `tls-client-auth-ca` is the sole inbound mTLS selector
for a client. This dedicated endpoint routing is what's scoped: only requests to
`/oauth/mtls/token` attempt to authenticate the caller via a presented client certificate --
requests to `/oauth/token` are never affected by this.

The underlying TLS-layer change, however, is **connector-wide, not per-endpoint**: enabling
this feature (`uaa.mtls-enabled`) reconfigures the whole embedded Tomcat connector to request a
client certificate on *every* TLS handshake to this UAA instance (`certificateVerification=
optionalNoCA`; see `MtlsClientAuthTomcatCustomizer`), regardless of which path the request is
ultimately routed to. Any TLS client connecting to any UAA endpoint will therefore be prompted
for a certificate during the handshake -- well-behaved clients (including Go's `crypto/tls`)
simply respond with an empty `Certificate` message if they have no certificate matching the
connector's advertised acceptable-issuer list, so this doesn't outright break other endpoints,
but it is a deployment-wide TLS-layer change, not one isolated to `/oauth/mtls/token`.

#### Deployment topology

UAA itself only ever sees the certificate presented by its *immediate* TLS peer -- whatever
that happens to be depends on how UAA is deployed:

* **Behind a Gorouter** with `forwarded_client_cert: sanitize_set` (the typical Cloud
Foundry deployment): the Gorouter terminates the client's TLS connection, validates it, and
forwards the client's certificate to UAA in the `X-Forwarded-Client-Cert` header over its own
backend mTLS connection. Here, UAA's immediate TLS peer is the Gorouter itself, not the
original client.
* **Direct connections**, e.g. an app connecting straight to UAA over BOSH DNS
(`uaa.service.cf.internal`) where Application Security Groups permit it, bypassing the
Gorouter entirely: UAA's immediate TLS peer *is* the original client.

`tls-client-auth-trusted-proxy-ca` determines which of the two topologies a *given client* uses --
the two are mutually exclusive per client, not two ways of satisfying the same requirement:

* **Not configured:** the client is direct-connection-only. UAA always authenticates it using the
certificate its immediate TLS peer actually presented during the handshake, and never consults
the `X-Forwarded-Client-Cert` header at all (even if one happens to be present -- e.g. noise
from an unrelated proxy in the network path).
* **Configured:** the client is proxy-only. UAA requires the `X-Forwarded-Client-Cert` header to
actually be present, and the genuine TLS peer that presented it to validate against this CA,
before trusting the header-derived certificate. A direct connection (no header) is always
rejected for this client, even if its own certificate happens to validate against the configured
CA.

An operator who needs both a Gorouter-fronted access pattern and a direct-connection access
pattern for what is conceptually "the same" workload registers **two separate UAA clients** -- one
with `tls-client-auth-trusted-proxy-ca` set (proxy path) and one without it (direct path) -- rather
than expecting one client to accept either.

#### Scoping a client to a specific org/space/app

Because Cloud Foundry's Diego instance-identity CA is shared across every app instance in a
foundation, any two clients configured with the same `tls-client-auth-ca` can otherwise
authenticate each other's certificates -- PKIX chain validation alone only proves a certificate
was issued by the configured CA, not that it belongs to *this* client specifically. Configure
`tls-client-auth-required-claims` to close this gap for a client that should only be reachable by
a specific subset of apps:

```yaml
tls-client-auth-claim-mappings:
- field: subject_ou
pattern: "space:(.+)"
claim: space_guid
tls-client-auth-required-claims:
space_guid: <specific-space-guid>
```

An operator who needs both a broadly-scoped client (e.g. the generic `instance-identity` client,
accepting any app in the foundation) and a narrowly-scoped one (e.g. limited to a single space)
registers them as two separate UAA clients, only the latter configuring
`tls-client-auth-required-claims`.

#### Configuration

Per-client properties (set via the client-admin API, `oauth.clients` bootstrap, or the client
admin UI, alongside the client's other properties such as `authorized-grant-types`):

The mTLS token endpoint is fixed at `/oauth/mtls/token`; it is not configurable. A client opts
into mTLS by configuring a nonblank `tls-client-auth-ca`. The client must use that endpoint and
present a certificate whose chain validates to the configured CA; no separate
`token-endpoint-auth-method` property is used or supported.

| Property | Required | Description |
|----------|----------|--------------|
| `tls-client-auth-ca` | yes | PEM-encoded CA certificate. This is the per-client mTLS selector: requests to the fixed `/oauth/mtls/token` endpoint authenticate with a presented leaf certificate only when it chains to this CA. |
| `tls-client-auth-trusted-proxy-ca` | conditional | PEM-encoded CA certificate the Gorouter's own backend mTLS certificate must chain to. Configuring this switches the client to the Gorouter/XFCC-forwarding-only topology (requiring the `X-Forwarded-Client-Cert` header) -- see "Deployment topology" above. Leave unset for a direct-connection-only client. |
| `tls-client-auth-required-claims` | no | Map of `claimName -> requiredValue`, checked against the values already produced by `tls-client-auth-claim-mappings`. When configured, authentication fails unless every entry matches exactly -- e.g. `{space_guid: "<specific-space-guid>"}` scopes this client to a single CF space, even if other clients share the same `tls-client-auth-ca`. |
| `tls-client-auth-claim-mappings` | no | List of `{field, pattern, claim}` mappings from certificate subject fields (`subject_cn`, `subject_ou`, `subject_o`) to JWT claim names. `subject_cn` and `subject_o` map their values directly; `pattern` is supported only for `subject_ou`, where it extracts a capture group. Patterns are UAA administrator-controlled configuration and are evaluated on every mTLS authentication request; use efficient Java regular expressions and avoid patterns with catastrophic backtracking. |
| `tls-client-auth-sub-template` | no | Template string rendered (using the mapped claim values) to produce the JWT `sub` claim. |
| `tls-client-auth-aud-templates` | no | List of template strings rendered to produce the JWT `aud` claim. |

Example (Gorouter-fronted; a Cloud Foundry app instance identity certificate mapped to
`cf_instance_guid`/`app_guid`/`space_guid`/`org_guid` claims):

```yaml
tls-client-auth-ca: <instance-identity CA certificate PEM>
tls-client-auth-trusted-proxy-ca: <Gorouter backend mTLS CA certificate PEM, e.g. service_cf_internal_ca>
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
```

For the direct-connection topology described above, omit `tls-client-auth-trusted-proxy-ca`
entirely rather than setting it -- configuring it at all switches this client to proxy-only.

## Configs

Here is a brief example of the `clients` section:

```yaml
oauth:
clients:
Expand Down Expand Up @@ -78,9 +200,11 @@ oauth:
]
}
```

The example configuration above with jwks_uri enables continuous trust to a running UAA.

Here is a brief example of the oauth providers section, where UAA is acting as a client.

```yaml
login:
oauth:
Expand All @@ -99,12 +223,13 @@ login:
The option jwtClientAuthentication creates during the proxy flow a client assertion which is based on OIDC private_key_jwt.

### Developer implementation

As a developer, you should use the [UAA documentation](https://docs.cloudfoundry.org/api/uaa/version/77.18.0/index.html#token). There is a description
about the new parameters client_assertion and client_assertion_type. In addition, you can check in the retrieved access_token tokens for the existence
of claim client_auth_method with value private_key_jwt, (client_auth_method=private_key). This claim should guarantee the used method of client
authentication. Tokens without this claim are authenticated with secrets. There might be use-cases where a stronger authentication mechanism is
about the new parameters client_assertion and client_assertion_type. In addition, you can check in the retrieved access_token tokens for the existence
of claim client_auth_method with value private_key_jwt, (client_auth_method=private_key). This claim should guarantee the used method of client
authentication. Tokens without this claim are authenticated with secrets. There might be use-cases where a stronger authentication mechanism is
required.

### Production use

The support of private_key_jwt (according to OIDC) for a production system is given with the end of Q4/2024.
The support of private_key_jwt (according to OIDC) for a production system is given with the end of Q4/2024.
34 changes: 34 additions & 0 deletions docs/UAA-Configuration-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ or `$CLOUDFOUNDRY_CONFIG_PATH/uaa.yml`.
| <a href="#oauthclientautoapprove"><img src="images/click-me.png" width="14" height="14"/></a> `oauth.client.autoapprove` | `[]`| Clients auto-approved for all scopes|
| <a href="#oauthuserauthorities"><img src="images/click-me.png" width="14" height="14"/></a> `oauth.user.authorities` | (see details)| Default authorities for new users|
| <a href="#clientmaxcount"><img src="images/click-me.png" width="14" height="14"/></a> `clientMaxCount` | `500`| Max clients returned by list endpoint|
| <a href="#uaamtls-enabled"><img src="images/click-me.png" width="14" height="14"/></a> `uaa.mtls-enabled` | `false`| Enables RFC 8705 mutual-TLS client authentication|

### Password Policy

Expand Down Expand Up @@ -1221,6 +1222,39 @@ client admin API (`/oauth/clients`).

---

### `uaa.mtls-enabled`

**Default:** `false`
**Source:** `@Value("${uaa.mtls-enabled:false}")` in [`SpringServletXmlBeansConfiguration`](../server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java), [`ClientAdminBootstrap`](../server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminBootstrap.java), [`ZoneEndpointsClientDetailsValidator`](../server/src/main/java/org/cloudfoundry/identity/uaa/zone/ZoneEndpointsClientDetailsValidator.java), [`MtlsClientAuthTomcatCustomizer`](../server/src/main/java/org/cloudfoundry/identity/uaa/web/tomcat/MtlsClientAuthTomcatCustomizer.java)
**Type:** `boolean`
Comment thread
rkoster marked this conversation as resolved.

Master switch enabling RFC 8705 mutual-TLS client authentication (`tls_client_auth`)
deployment-wide. This is **connector-wide**: it affects every TLS connection to this UAA
instance, not just requests to the mTLS token endpoint (`/oauth/mtls/token`).
[`SpringServletXmlBeansConfiguration`](../server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlBeansConfiguration.java)
also uses this value to wire
[`ClientAdminEndpointsValidator`](../server/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAdminEndpointsValidator.java)'s
`mtlsEnabled` constructor argument.

When `true`, the embedded Tomcat connector is reconfigured to request a client certificate
during every TLS handshake (`certificateVerification=optionalNoCA`), without validating it
against any CA at the transport layer -- the trust decision is deferred entirely to per-client
application logic (see [`docs/UAA-Client-Authentication.md`](UAA-Client-Authentication.md) for
the per-client `tls-client-auth-*` properties). Enabling this also switches the connector to the
FIPS BouncyCastle JSSE provider, required for TLS 1.3 client-certificate support (OpenJDK's JSSE
does not implement server-side TLS 1.3 post-handshake client-certificate requests).

When `false` (the default), no client certificate is requested at the TLS layer at all, and any
client configured with a `tls-client-auth-ca` property fails validation at creation/update time.

```yaml
uaa.mtls-enabled: true
```

[Back to table](#oauth-clients--users)

---

### `password.policy.global.minLength`

**Default:** `0`
Expand Down
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jacoco = "4.0.2"
jackson = "3.1.5"
jackson2 = "2.21.5"
jacocoAgent = "0.8.15"
javaBuildpackClientCertificateMapper = "2.0.1"
nimbusJwt = "10.9.1"
opensaml = "5.2.3"
orgJson = "20260814"
Expand Down Expand Up @@ -70,6 +71,9 @@ bouncyCastlePkixFips = { module = "org.bouncycastle:bcpkix-fips", version.ref =
bouncyCastleTlsFips = { module = "org.bouncycastle:bctls-fips", version.ref = "bouncyCastleTls" }
bouncyCastleUtilFips = { module = "org.bouncycastle:bcutil-fips", version.ref = "bouncyCastleUtil" }

# CloudFoundry
javaBuildpackClientCertificateMapper = { module = "org.cloudfoundry:java-buildpack-client-certificate-mapper-jakarta", version.ref = "javaBuildpackClientCertificateMapper" }

# Eclipse JGit
eclipseJgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "eclipseJgit" }

Expand Down Expand Up @@ -164,6 +168,7 @@ springBootStarterMail = { module = "org.springframework.boot:spring-boot-starter
springBootStarterTest = { module = "org.springframework.boot:spring-boot-starter-test" }
springBootStarterTomcatRuntime = { module = "org.springframework.boot:spring-boot-starter-tomcat-runtime" }
springBootStarterWeb = { module = "org.springframework.boot:spring-boot-starter-web" }
springBootTomcat = { module = "org.springframework.boot:spring-boot-tomcat" }
springBootTransaction = { module = "org.springframework.boot:spring-boot-transaction" }

# Spring Data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.cloudfoundry.identity.uaa.account;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.cloudfoundry.identity.uaa.constants.ClientAuthentication;

import java.util.Arrays;
import java.util.Map;

@Data
@NoArgsConstructor
public class OpenIdConfiguration {
Expand All @@ -19,7 +23,7 @@ public class OpenIdConfiguration {
private String tokenUrl;

@JsonProperty("token_endpoint_auth_methods_supported")
private String[] tokenAMR = new String[]{ClientAuthentication.CLIENT_SECRET_BASIC, ClientAuthentication.CLIENT_SECRET_POST, ClientAuthentication.PRIVATE_KEY_JWT};
private String[] tokenAMR = new String[]{ClientAuthentication.CLIENT_SECRET_BASIC, ClientAuthentication.CLIENT_SECRET_POST, ClientAuthentication.PRIVATE_KEY_JWT, ClientAuthentication.TLS_CLIENT_AUTH};
Comment thread
rkoster marked this conversation as resolved.

@JsonProperty("token_endpoint_auth_signing_alg_values_supported")
private String[] tokenEndpointAuthSigningValues = new String[]{"RS256", "HS256"};
Expand Down Expand Up @@ -67,12 +71,25 @@ public class OpenIdConfiguration {
@JsonProperty("code_challenge_methods_supported")
private String[] codeChallengeMethodsSupported = new String[]{"S256", "plain"};

@JsonProperty("mtls_endpoint_aliases")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Map<String, String> mtlsEndpointAliases;

public OpenIdConfiguration(final String contextPath, final String issuer) {
this(contextPath, issuer, true);
}

public OpenIdConfiguration(final String contextPath, final String issuer, final boolean mtlsEnabled) {
this.issuer = issuer;
this.authUrl = contextPath + "/oauth/authorize";
this.tokenUrl = contextPath + "/oauth/token";
this.userInfoUrl = contextPath + "/userinfo";
this.jwksUri = contextPath + "/token_keys";
this.logoutEndpoint = contextPath + "/logout.do";
if (!mtlsEnabled) {
this.tokenAMR = Arrays.stream(this.tokenAMR)
.filter(method -> !ClientAuthentication.TLS_CLIENT_AUTH.equals(method))
.toArray(String[]::new);
}
}
}
Loading
Loading