UAA acts as OAuth2 / OIDC server and this requires the separate authentication of users and clients. This document focuses on the clients and in detail on the key based client authentication, because this has special behaviors. In RFC 6749 the password of a client is specified as so-called secret (parameter client_secret). Its possession 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 HTTP header and/or the body. In case of header the Authorization header is used, but the encoding of the secret needs to be done according to the RFC 6749. UAA fixed this behavior with #778. The OIDC standard defines more authentication mechanism, see section 9. The usage of secrets via client_secret_basic and client_secret_post is easy to set up and easy to use, however if system to system communication is in use, this can get a security problem, because it will be hard to change secrets in running systems. The use of many secrets is not supported, also because the check can only be done sequentially. The exchange of a secret is a security problem in it self. Therefore the newer standards define further token based authentication mechanism for OAuth2 clients. They are:
- private_key_jwt OIDC core standard and RFC 7523 from OAuth2 standard
- tls_client_auth RFC 8705
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.
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. The JWT bearer trust is based on parameters tokenKey and/or tokenKeyUrl parameter, part of the identity providers configuration section. The signature check of a client jwt can be verified with a set of public keys and this set can contain many keys because each key has its own kid (key id). The keys can be stored in UAA own persistency or with 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. UAA provides its own jwks_uri with endpoint /token_keys. The content of this endpoint is JWKS.
The content of the JWT (parameter client_assertion) can be different. The difference is defined by the standards. The OIDC core standard simplify the structure, so that subject, issuer content 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 the structure is more complex but with seperated issuer and subject there can be more than one entry of federated credential.
The new parameters for JWKS Trust in UAA clients are:
- jwks_uri
- jwks
This should allow a continuous trust between a UAA to UAA communication, e.g. using own UAA instances or within a UAA using different zones.
The new parameter for federated Credentials in UAA clients is (Work in progress parameter):
- jwt_creds
Not yet defined a release date.
Here is a brief example of the clients
section:
oauth:
clients:
uaa-trust-uri:
authorities: scim.zones,uaa.zones.read,uaa.zones.write,uaa.admin,clients.read,clients.write,clients.secret,zones.read,zones.uaa.admin
authorized-grant-types: client_credentials
id: uaa_trust
scope: none
jwks_uri: http://localhost:8080/uaa/token_keys
uaa-trust-keys:
authorities: scim.zones,uaa.zones.read,uaa.zones.write,uaa.admin,clients.read,clients.write,clients.secret,zones.read,zones.uaa.admin
authorized-grant-types: client_credentials
id: uaa_trust
scope: none
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "legacy-token-key",
"alg": "RS256",
"n": "qMClJXznycV2bQ1pFbN8W-AWSYhpS2MVAGhkWNlmxv2Ix0_-n6zjivjdoxcq7RJR4kVycoVeD07DiWElYSnQLdeQPgKAcBiwilR30UyyDTKcqDQQ5rkCg2ONlwV0aMsg74KaXeXsV653ASs3FYEtuS1aD_Db5-FyXF8HkHo8xy19NUnqsDWQnh1Hhklynxu2tvW0fw2oDE1pwNl-WLEVPtlcpCtf4VSv-GawtBiI6xmYsGBMC9w29ESHFqPw0NSCRhlyJf6rDBNH_766mzK_vEzA4rzGTBEUqDxTg_8JpRhh9D3qljSsmqCtpQoloOAaUKCqSJb_hKPspe-7r9cYmw"
}
]
}
The example config 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 client.
login:
oauth:
providers:
uaa.proxy:
type: oidc1.0
passwordGrantEnabled: true
discoveryUrl: http://localhost:8080/uaa/oauth/token/.well-known/openid-configuration
issuer: http://localhost:8080/uaa/oauth/token
linkText: Login with another UAA
relyingPartyId: uaa-trust-uri
jwtClientAuthentication: true
showLinkText: true
The option jwtClientAuthentication creates during the proxy flow a client assertion which is based on OIDC private_key_jwt.
As developer, you should use the UAA documentation. 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 required.
The support of private_key_jwt (according to OIDC) for a production is given with end of Q4/2024.