From c3a77ce664c1d7d48ba9f4e6949c74fc752c0c7e Mon Sep 17 00:00:00 2001 From: strehle Date: Tue, 3 Oct 2023 19:34:34 +0200 Subject: [PATCH] update --- .../identity/uaa/account/OpenIdConfigurationTests.java | 2 +- .../identity/uaa/account/OpenIdConfiguration.json | 3 ++- .../identity/uaa/login/TokenEndpointDocs.java | 8 ++++---- .../uaa/mock/token/JwtBearerGrantEndpointDocs.java | 4 +++- .../uaa/mock/token/JwtBearerGrantMockMvcTests.java | 6 ++++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/model/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConfigurationTests.java b/model/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConfigurationTests.java index 922db932255..b742fdd6d05 100644 --- a/model/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConfigurationTests.java +++ b/model/src/test/java/org/cloudfoundry/identity/uaa/account/OpenIdConfigurationTests.java @@ -28,7 +28,7 @@ void defaultClaims() { assertEquals("issuer", defaultConfig.getIssuer()); assertEquals("/uaa/oauth/authorize", defaultConfig.getAuthUrl()); assertEquals("/uaa/oauth/token", defaultConfig.getTokenUrl()); - assertArrayEquals(new String[]{"client_secret_basic", "client_secret_post"}, defaultConfig.getTokenAMR()); + assertArrayEquals(new String[]{"client_secret_basic", "client_secret_post", "private_key_jwt"}, defaultConfig.getTokenAMR()); assertArrayEquals(new String[]{"RS256", "HS256"}, defaultConfig.getTokenEndpointAuthSigningValues()); assertEquals("/uaa/userinfo", defaultConfig.getUserInfoUrl()); assertEquals("/uaa/token_keys", defaultConfig.getJwksUri()); diff --git a/model/src/test/resources/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.json b/model/src/test/resources/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.json index 3e8ec3f6018..2a102387fae 100644 --- a/model/src/test/resources/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.json +++ b/model/src/test/resources/org/cloudfoundry/identity/uaa/account/OpenIdConfiguration.json @@ -4,7 +4,8 @@ "token_endpoint": "/oauth/token", "token_endpoint_auth_methods_supported": [ "client_secret_basic", - "client_secret_post" + "client_secret_post", + "private_key_jwt" ], "token_endpoint_auth_signing_alg_values_supported": [ "RS256", diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/TokenEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/TokenEndpointDocs.java index 178eabb0991..2461cc743d0 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/TokenEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/login/TokenEndpointDocs.java @@ -102,7 +102,7 @@ class TokenEndpointDocs extends AbstractTokenMockMvcTests { private final ParameterDescriptor grantTypeParameter = parameterWithName(GRANT_TYPE).required().type(STRING).description("OAuth 2 grant type"); private final ParameterDescriptor clientIdParameter = parameterWithName(CLIENT_ID).optional(null).type(STRING).description("A unique string representing the registration information provided by the client, the recipient of the token. Optional if it is passed as part of the Basic Authorization header or as part of the client_assertion."); - private final ParameterDescriptor clientSecretParameter = parameterWithName("client_secret").optional(null).type(STRING).description("The secret passphrase configured for the OAuth client. Optional if it is passed as part of the Basic Authorization header."); + private final ParameterDescriptor clientSecretParameter = parameterWithName("client_secret").optional(null).type(STRING).description("The [secret passphrase configured](#change-secret) for the OAuth client. Optional if it is passed as part of the Basic Authorization header or if client_assertion is sent as part of private_key_jwt authentication."); private final ParameterDescriptor opaqueFormatParameter = parameterWithName(REQUEST_TOKEN_FORMAT).optional(null).type(STRING).description("Can be set to `" + OPAQUE.getStringValue() + "` to retrieve an opaque and revocable token or to `" + JWT.getStringValue() + "` to retrieve a JWT token. If not set the zone setting config.tokenPolicy.jwtRevocable is used."); private final ParameterDescriptor scopeParameter = parameterWithName(SCOPE).optional(null).type(STRING).description("The list of scopes requested for the token. Use when you wish to reduce the number of scopes the token will have."); private final ParameterDescriptor loginHintParameter = parameterWithName("login_hint").optional(null).type(STRING).description("UAA 75.5.0 Indicates the identity provider to be used. The passed string has to be a URL-Encoded JSON Object, containing the field `origin` with value as `origin_key` of an identity provider. Note that this identity provider must support the grant type `password`."); @@ -117,9 +117,9 @@ class TokenEndpointDocs extends AbstractTokenMockMvcTests { private final FieldDescriptor jtiFieldDescriptor = fieldWithPath("jti").description("A globally unique identifier for this access token. This identifier is used when [revoking tokens](#revoke-tokens)."); private final FieldDescriptor tokenTypeFieldDescriptor = fieldWithPath("token_type").description("The type of the access token issued. This field is mandated in [RFC 6749](https://tools.ietf.org/html/rfc6749#section-7.1). In the UAA, the only supported `token_type` is `bearer`."); - private final ParameterDescriptor clientAssertionType = parameterWithName(JwtClientAuthentication.CLIENT_ASSERTION_TYPE).optional(null).description("UAA 76.22.0 [RFC 7523](https://tools.ietf.org/html/rfc7523) describes the type, constant `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` if `client_assertion` parameter is present.").attributes(key("constraints").value("Optional"), key("type").value(STRING)); + private final ParameterDescriptor clientAssertionType = parameterWithName(JwtClientAuthentication.CLIENT_ASSERTION_TYPE).optional(null).description("UAA 76.22.0 [RFC 7523](https://tools.ietf.org/html/rfc7523) describes the type. Must be set to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` if `client_assertion` parameter is present.").attributes(key("constraints").value("Optional"), key("type").value(STRING)); - private final ParameterDescriptor clientAssertion = parameterWithName(JwtClientAuthentication.CLIENT_ASSERTION).optional(null).description("UAA 76.22.0 Client authentication using method [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) optional instead of client_secret_basic or client_secret_post, used as replacement for secrets. The client authentication is used already for the OIDC proxy token requests.").attributes(key("constraints").value("Optional"), key("type").value(STRING)); + private final ParameterDescriptor clientAssertion = parameterWithName(JwtClientAuthentication.CLIENT_ASSERTION).optional(null).description("UAA 76.22.0 Client authentication using method [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) optional as replacement of methods client_secret_basic or client_secret_post using secrets. The client needs to have a valid [JWT confiuration](#change-client-jwt) for trust to JWT in client_assertion.").attributes(key("constraints").value("Optional"), key("type").value(STRING)); private final String codeDescription = "the authorization code, obtained from `/oauth/authorize`, issued for the user"; @@ -651,7 +651,7 @@ void refreshToken() throws Exception { Snippet requestParameters = requestParameters( grantTypeParameter.description("the type of authentication being used to obtain the token, in this case `refresh_token`"), clientIdParameter, - clientSecretParameter, + clientSecretParameter.description("Optional and can be omitted if token before was requested using [PKCE](https://tools.ietf.org/html/rfc7636) with `code_challange_method=S256` without a secret or client_assertion is used for private_key_jwt client authentication."), clientAssertion, clientAssertionType, parameterWithName("refresh_token").required().type(STRING).description("the refresh_token that was returned along with the access token."), diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantEndpointDocs.java index da7d4d030c3..9d9872daef7 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantEndpointDocs.java @@ -76,7 +76,9 @@ void document_jwt_bearer_grant() throws Exception { Snippet requestParameters = requestParameters( parameterWithName("assertion").type(STRING).required().description("JWT token identifying representing the user to be authenticated"), parameterWithName("client_id").type(STRING).required().description("Required, client with "), - parameterWithName("client_secret").type(STRING).required().description("Required unless a basic authorization header is used"), + parameterWithName("client_secret").type(STRING).optional(null).description("The [secret passphrase configured](#change-secret) for the OAuth client. Optional if it is passed as part of the Basic Authorization header or if client_assertion is sent as part of private_key_jwt authentication."), + parameterWithName("client_assertion").type(STRING).optional(null).description("UAA 76.22.0 Client authentication using method [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) optional as replacement of methods client_secret_basic or client_secret_post using secrets. The client needs to have a valid [JWT confiuration](#change-client-jwt) for trust to JWT in client_assertion."), + parameterWithName("client_assertion_type").type(STRING).optional(null).description("UAA 76.22.0 [RFC 7523](https://tools.ietf.org/html/rfc7523) describes the type. Must be set to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` if `client_assertion` parameter is present."), parameterWithName("grant_type").type(STRING).required().description("Must be set to `"+ GRANT_TYPE_JWT_BEARER+"`"), parameterWithName("scope").type(STRING).optional(null).description("Optional parameter to limit the number of scopes in the `scope` claim of the access token"), parameterWithName("response_type").type(STRING).optional(null).description("May be set to `token` or `token id_token` or `id_token`"), diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantMockMvcTests.java index 814a4acc17d..36f534d8aaa 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/JwtBearerGrantMockMvcTests.java @@ -15,13 +15,11 @@ package org.cloudfoundry.identity.uaa.mock.token; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.core.type.TypeReference; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.mock.util.JwtTokenUtils; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils; import org.cloudfoundry.identity.uaa.oauth.KeyInfoService; -import org.cloudfoundry.identity.uaa.oauth.token.CompositeToken; import org.cloudfoundry.identity.uaa.oauth.token.TokenConstants; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; @@ -238,6 +236,8 @@ ResultActions perform_grant_in_zone(IdentityZone theZone, String assertion) thro .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .param("client_id", client.getClientId()) .param("client_secret", client.getClientSecret()) + .param("client_assertion", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjU4ZDU1YzUwMGNjNmI1ODM3OTYxN2UwNmU3ZGVjNmNhIn0.eyJzdWIiOiJsb2dpbiIsImlzcyI6ImxvZ2luIiwianRpIjoiNThkNTVjNTAwY2M2YjU4Mzc5NjE3ZTA2ZTdhZmZlZSIsImV4cCI6MTIzNDU2NzgsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC91YWEvb2F1dGgvdG9rZW4ifQ.jwWw0OKZecd4ZjtwQ_ievqBVrh2SieqMF6vY74Oo5H6v-Ibcmumq96NLNtoUEwaAEQQOHb8MWcC8Gwi9dVQdCrtpomC86b_LKkihRBSKuqpw0udL9RMH5kgtC04ctsN0yZNifUWMP85VHn97Ual5eZ2miaBFob3H5jUe98CcBj1TSRehr64qBFYuwt9vD19q6U-ONhRt0RXBPB7ayHAOMYtb1LFIzGAiKvqWEy9f-TBPXSsETjKkAtSuM-WVWi4EhACMtSvI6iJN15f7qlverRSkGIdh1j2vPXpKKBJoRhoLw6YqbgcUC9vAr17wfa_POxaRHvh9JPty0ZXLA4XPtA") + .param("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") .param(GRANT_TYPE, GRANT_TYPE_JWT_BEARER) .param(TokenConstants.REQUEST_TOKEN_FORMAT, TokenConstants.TokenFormat.OPAQUE.getStringValue()) .param("response_type", "token id_token") @@ -260,6 +260,8 @@ private String performJWTBearerGrantForJWT(IdentityZone theZone, String assertio .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) .param("client_id", client.getClientId()) .param("client_secret", client.getClientSecret()) + .param("client_assertion", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjU4ZDU1YzUwMGNjNmI1ODM3OTYxN2UwNmU3ZGVjNmNhIn0.eyJzdWIiOiJsb2dpbiIsImlzcyI6ImxvZ2luIiwianRpIjoiNThkNTVjNTAwY2M2YjU4Mzc5NjE3ZTA2ZTdhZmZlZSIsImV4cCI6MTIzNDU2NzgsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC91YWEvb2F1dGgvdG9rZW4ifQ.jwWw0OKZecd4ZjtwQ_ievqBVrh2SieqMF6vY74Oo5H6v-Ibcmumq96NLNtoUEwaAEQQOHb8MWcC8Gwi9dVQdCrtpomC86b_LKkihRBSKuqpw0udL9RMH5kgtC04ctsN0yZNifUWMP85VHn97Ual5eZ2miaBFob3H5jUe98CcBj1TSRehr64qBFYuwt9vD19q6U-ONhRt0RXBPB7ayHAOMYtb1LFIzGAiKvqWEy9f-TBPXSsETjKkAtSuM-WVWi4EhACMtSvI6iJN15f7qlverRSkGIdh1j2vPXpKKBJoRhoLw6YqbgcUC9vAr17wfa_POxaRHvh9JPty0ZXLA4XPtA") + .param("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") .param(GRANT_TYPE, GRANT_TYPE_JWT_BEARER) .param(TokenConstants.REQUEST_TOKEN_FORMAT, TokenConstants.TokenFormat.JWT.getStringValue()) .param("response_type", "token id_token")