diff --git a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/ApplicationDTO.java b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/ApplicationDTO.java index a66d1be779..f7d980a4e1 100644 --- a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/ApplicationDTO.java +++ b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/ApplicationDTO.java @@ -65,6 +65,7 @@ public class ApplicationDTO { private String jwksUri = null; private String tokenEndpointAuthMethod = null; + private Boolean tokenEndpointAllowReusePvtKeyJwt = null; private String tokenEndpointAuthSigningAlg = null; private String sectorIdentifierUri = null; private String idTokenSignedResponseAlg = null; @@ -292,6 +293,17 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + @ApiModelProperty(value = "") + @JsonProperty("token_endpoint_allow_reuse_pvt_key_jwt") + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } @ApiModelProperty(value = "") @JsonProperty("token_endpoint_auth_signing_alg") diff --git a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/RegistrationRequestDTO.java b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/RegistrationRequestDTO.java index 92e34409e4..e42227c3b0 100644 --- a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/RegistrationRequestDTO.java +++ b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/RegistrationRequestDTO.java @@ -49,6 +49,7 @@ public class RegistrationRequestDTO { private String extTokenType = null; private String tokenEndpointAuthMethod = null; private String tokenEndpointAuthSigningAlg = null; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String sectorIdentifierUri = null; private String idTokenSignedResponseAlg = null; private String idTokenEncryptedResponseAlg = null; @@ -332,6 +333,18 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + @ApiModelProperty(value = "") + @JsonProperty("token_endpoint_allow_reuse_pvt_key_jwt") + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + @ApiModelProperty(value = "") @JsonProperty("token_endpoint_auth_signing_alg") diff --git a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/UpdateRequestDTO.java b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/UpdateRequestDTO.java index 81471cc237..085eb32d26 100644 --- a/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/UpdateRequestDTO.java +++ b/components/org.wso2.carbon.identity.api.server.dcr/src/gen/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/dto/UpdateRequestDTO.java @@ -36,6 +36,7 @@ public class UpdateRequestDTO { private boolean extPublicClient; private String extTokenType = null; private String tokenEndpointAuthMethod = null; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String tokenEndpointAuthSigningAlg = null; private String sectorIdentifierUri = null; private String idTokenSignedResponseAlg = null; @@ -241,6 +242,18 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + @ApiModelProperty(value = "") + @JsonProperty("token_endpoint_allow_reuse_pvt_key_jwt") + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + @ApiModelProperty(value = "") @JsonProperty("token_endpoint_auth_signing_alg") public String getTokenEndpointAuthSigningAlg() { diff --git a/components/org.wso2.carbon.identity.api.server.dcr/src/main/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/util/DCRMUtils.java b/components/org.wso2.carbon.identity.api.server.dcr/src/main/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/util/DCRMUtils.java index 8e46d6c25f..23e87ffa55 100644 --- a/components/org.wso2.carbon.identity.api.server.dcr/src/main/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/util/DCRMUtils.java +++ b/components/org.wso2.carbon.identity.api.server.dcr/src/main/java/org/wso2/carbon/identity/oauth2/dcr/endpoint/util/DCRMUtils.java @@ -81,6 +81,8 @@ public static ApplicationRegistrationRequest getApplicationRegistrationRequest( appRegistrationRequest.setExtTokenType(registrationRequestDTO.getExtTokenType()); appRegistrationRequest.setJwksURI(registrationRequestDTO.getJwksUri()); appRegistrationRequest.setTokenEndpointAuthMethod(registrationRequestDTO.getTokenEndpointAuthMethod()); + appRegistrationRequest.setTokenEndpointAllowReusePvtKeyJwt(registrationRequestDTO + .isTokenEndpointAllowReusePvtKeyJwt()); appRegistrationRequest.setTokenEndpointAuthSignatureAlgorithm (registrationRequestDTO.getTokenEndpointAuthSigningAlg()); appRegistrationRequest.setSectorIdentifierURI(registrationRequestDTO.getSectorIdentifierUri()); @@ -125,6 +127,8 @@ public static ApplicationUpdateRequest getApplicationUpdateRequest(UpdateRequest applicationUpdateRequest.setExtTokenType(updateRequestDTO.getExtTokenType()); applicationUpdateRequest.setJwksURI(updateRequestDTO.getJwksUri()); applicationUpdateRequest.setTokenEndpointAuthMethod(updateRequestDTO.getTokenEndpointAuthMethod()); + applicationUpdateRequest.setTokenEndpointAllowReusePvtKeyJwt( + updateRequestDTO.isTokenEndpointAllowReusePvtKeyJwt()); applicationUpdateRequest.setTokenEndpointAuthSignatureAlgorithm (updateRequestDTO.getTokenEndpointAuthSigningAlg()); applicationUpdateRequest.setSectorIdentifierURI(updateRequestDTO.getSectorIdentifierUri()); @@ -235,6 +239,7 @@ public static ApplicationDTO getApplicationDTOFromApplication(Application applic applicationDTO.setExtTokenType(application.getExtTokenType()); applicationDTO.setJwksUri(application.getJwksURI()); applicationDTO.setTokenEndpointAuthMethod(application.getTokenEndpointAuthMethod()); + applicationDTO.setTokenEndpointAllowReusePvtKeyJwt(application.isTokenEndpointAllowReusePvtKeyJwt()); applicationDTO.setTokenEndpointAuthSigningAlg(application.getTokenEndpointAuthSignatureAlgorithm()); applicationDTO.setSectorIdentifierUri(application.getSectorIdentifierURI()); applicationDTO.setIdTokenSignedResponseAlg(application.getIdTokenSignatureAlgorithm()); diff --git a/components/org.wso2.carbon.identity.oauth.common/src/main/java/org/wso2/carbon/identity/oauth/common/OAuthConstants.java b/components/org.wso2.carbon.identity.oauth.common/src/main/java/org/wso2/carbon/identity/oauth/common/OAuthConstants.java index 57e7feac1e..94b888cec6 100644 --- a/components/org.wso2.carbon.identity.oauth.common/src/main/java/org/wso2/carbon/identity/oauth/common/OAuthConstants.java +++ b/components/org.wso2.carbon.identity.oauth.common/src/main/java/org/wso2/carbon/identity/oauth/common/OAuthConstants.java @@ -622,6 +622,7 @@ public static class OIDCConfigProperties { public static final String TOKEN_BINDING_VALIDATION = "tokenBindingValidation"; public static final String TOKEN_BINDING_TYPE_NONE = "None"; public static final String TOKEN_AUTH_METHOD = "tokenEndpointAuthMethod"; + public static final String TOKEN_EP_ALLOW_REUSE_PVT_KEY_JWT = "tokenEndpointAllowReusePvtKeyJwt"; public static final String TOKEN_AUTH_SIGNATURE_ALGORITHM = "tokenEndpointAuthSigningAlg"; public static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; public static final String ID_TOKEN_SIGNATURE_ALGORITHM = "idTokenSignedResponseAlg"; @@ -636,7 +637,14 @@ public static class OIDCConfigProperties { public static final String IS_SUBJECT_TOKEN_ENABLED = "isSubjectTokenEnabled"; public static final String SUBJECT_TOKEN_EXPIRY_TIME = "subjectTokenExpiryTime"; public static final int SUBJECT_TOKEN_EXPIRY_TIME_VALUE = 180; - + public static final String PREVENT_TOKEN_REUSE = "PreventTokenReuse"; + public static final boolean DEFAULT_VALUE_FOR_PREVENT_TOKEN_REUSE = true; + // Name of the {@code JWTClientAuthenticatorConfig} resource type in the Configuration Management API. + public static final String JWT_CONFIGURATION_RESOURCE_TYPE_NAME = "PK_JWT_CONFIGURATION"; + // Name of the {@code JWTClientAuthenticatorConfig} resource (per tenant) in the Configuration Management API. + public static final String JWT_CONFIGURATION_RESOURCE_NAME = "TENANT_PK_JWT_CONFIGURATION"; + public static final String PVT_KEY_JWT_CLIENT_AUTHENTICATOR_CLASS_NAME = "PrivateKeyJWTClientAuthenticator"; + public static final String ENABLE_TOKEN_REUSE = "EnableTokenReuse"; private OIDCConfigProperties() { } diff --git a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/Application.java b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/Application.java index bb555c1f16..7f0d3907f2 100644 --- a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/Application.java +++ b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/Application.java @@ -46,6 +46,7 @@ public class Application implements Serializable { private String extTokenType = null; private String jwksURI = null; private String tokenEndpointAuthMethod = null; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String tokenEndpointAuthSignatureAlgorithm = null; private String sectorIdentifierURI = null; private String idTokenSignatureAlgorithm = null; @@ -253,6 +254,16 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + public String getTokenEndpointAuthSignatureAlgorithm() { return tokenEndpointAuthSignatureAlgorithm; diff --git a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationRegistrationRequest.java b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationRegistrationRequest.java index af1666e651..068fa18637 100644 --- a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationRegistrationRequest.java +++ b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationRegistrationRequest.java @@ -52,6 +52,7 @@ public class ApplicationRegistrationRequest implements Serializable { private String jwksURI; private String softwareStatement; private String tokenEndpointAuthMethod; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String tokenEndpointAuthSignatureAlgorithm; private String sectorIdentifierURI; private String idTokenSignatureAlgorithm; @@ -380,6 +381,16 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + public String getTokenEndpointAuthSignatureAlgorithm() { return tokenEndpointAuthSignatureAlgorithm; diff --git a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationUpdateRequest.java b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationUpdateRequest.java index b98772d5dd..443821cd55 100644 --- a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationUpdateRequest.java +++ b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/bean/ApplicationUpdateRequest.java @@ -48,6 +48,7 @@ public class ApplicationUpdateRequest implements Serializable { private String jwksURI = null; private String softwareStatement; private String tokenEndpointAuthMethod; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String tokenEndpointAuthSignatureAlgorithm; private String sectorIdentifierURI; private String idTokenSignatureAlgorithm; @@ -305,6 +306,16 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + public String getTokenEndpointAuthSignatureAlgorithm() { return tokenEndpointAuthSignatureAlgorithm; diff --git a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/service/DCRMService.java b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/service/DCRMService.java index 6aa00ca66a..994bd068fa 100644 --- a/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/service/DCRMService.java +++ b/components/org.wso2.carbon.identity.oauth.dcr/src/main/java/org/wso2/carbon/identity/oauth/dcr/service/DCRMService.java @@ -351,6 +351,7 @@ public Application updateApplication(ApplicationUpdateRequest updateRequest, Str if (updateRequest.getTokenEndpointAuthMethod() != null) { appDTO.setTokenEndpointAuthMethod(updateRequest.getTokenEndpointAuthMethod()); } + appDTO.setTokenEndpointAllowReusePvtKeyJwt(updateRequest.isTokenEndpointAllowReusePvtKeyJwt()); if (updateRequest.getTokenEndpointAuthSignatureAlgorithm() != null) { appDTO.setTokenEndpointAuthSignatureAlgorithm (updateRequest.getTokenEndpointAuthSignatureAlgorithm()); @@ -670,6 +671,7 @@ private Application buildResponse(OAuthConsumerAppDTO createdApp, String tenantD application.setExtTokenType(createdApp.getTokenType()); application.setJwksURI(createdApp.getJwksURI()); application.setTokenEndpointAuthMethod(createdApp.getTokenEndpointAuthMethod()); + application.setTokenEndpointAllowReusePvtKeyJwt(createdApp.isTokenEndpointAllowReusePvtKeyJwt()); application.setTokenEndpointAuthSignatureAlgorithm(createdApp.getTokenEndpointAuthSignatureAlgorithm()); application.setSectorIdentifierURI(createdApp.getSectorIdentifierURI()); application.setIdTokenSignatureAlgorithm(createdApp.getIdTokenSignatureAlgorithm()); @@ -764,6 +766,7 @@ private OAuthConsumerAppDTO createOAuthApp(ApplicationRegistrationRequest regist if (registrationRequest.getTokenEndpointAuthMethod() != null) { oAuthConsumerApp.setTokenEndpointAuthMethod(registrationRequest.getTokenEndpointAuthMethod()); } + oAuthConsumerApp.setTokenEndpointAllowReusePvtKeyJwt(registrationRequest.isTokenEndpointAllowReusePvtKeyJwt()); if (registrationRequest.getTokenEndpointAuthSignatureAlgorithm() != null) { oAuthConsumerApp.setTokenEndpointAuthSignatureAlgorithm (registrationRequest.getTokenEndpointAuthSignatureAlgorithm()); diff --git a/components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuthAdminService.wsdl b/components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuthAdminService.wsdl old mode 100644 new mode 100755 index 8b9539eddc..a1b5a18871 --- a/components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuthAdminService.wsdl +++ b/components/org.wso2.carbon.identity.oauth.stub/src/main/resources/OAuthAdminService.wsdl @@ -432,6 +432,7 @@ + diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java old mode 100644 new mode 100755 index 068ac62c57..3129f55459 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImpl.java @@ -107,6 +107,7 @@ import static org.wso2.carbon.identity.oauth.OAuthUtil.handleErrorWithExceptionType; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OauthAppStates.APP_STATE_ACTIVE; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OauthAppStates.APP_STATE_DELETED; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.PRIVATE_KEY_JWT; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenBindings.NONE; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.buildScopeString; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.getTenantId; @@ -429,6 +430,13 @@ OAuthConsumerAppDTO registerAndRetrieveOAuthApplicationData(OAuthConsumerAppDTO } app.setTokenEndpointAuthMethod(tokenEndpointAuthMethod); } + Boolean tokenEndpointAllowReusePvtKeyJwt = application.isTokenEndpointAllowReusePvtKeyJwt(); + if (isInvalidTokenEPReusePvtKeyJwtRequest(tokenEndpointAuthMethod, + tokenEndpointAllowReusePvtKeyJwt)) { + throw handleClientError(INVALID_REQUEST, "Requested client authentication method " + + "incompatible with the Private Key JWT Reuse config value."); + } + app.setTokenEndpointAllowReusePvtKeyJwt(tokenEndpointAllowReusePvtKeyJwt); String tokenEndpointAuthSigningAlgorithm = application.getTokenEndpointAuthSignatureAlgorithm(); if (StringUtils.isNotEmpty(tokenEndpointAuthSigningAlgorithm)) { if (isFAPIConformanceEnabled) { @@ -855,6 +863,13 @@ void updateConsumerApplication(OAuthConsumerAppDTO consumerAppDTO, boolean enabl } oAuthAppDO.setTokenEndpointAuthMethod(tokenEndpointAuthMethod); + Boolean tokenEndpointAllowReusePvtKeyJwt = consumerAppDTO.isTokenEndpointAllowReusePvtKeyJwt(); + if (isInvalidTokenEPReusePvtKeyJwtRequest(tokenEndpointAuthMethod, tokenEndpointAllowReusePvtKeyJwt)) { + throw handleClientError(INVALID_REQUEST, "Requested client authentication method " + + "incompatible with the Private Key JWT Reuse config value."); + } + oAuthAppDO.setTokenEndpointAllowReusePvtKeyJwt(tokenEndpointAllowReusePvtKeyJwt); + String tokenEndpointAuthSignatureAlgorithm = consumerAppDTO.getTokenEndpointAuthSignatureAlgorithm(); if (StringUtils.isNotEmpty(tokenEndpointAuthSignatureAlgorithm)) { if (isFAPIConformanceEnabled) { @@ -2492,6 +2507,24 @@ private void handleInternalTokenRevocation(String consumerKey, Properties proper } } + /** + * Return whether the request of updating the tokenEndpointAllowReusePvtKeyJwt is valid. + * + * @param tokenEndpointAuthMethod token endpoint client authentication method. + * @param tokenEndpointAllowReusePvtKeyJwt During client authentication whether to reuse private key JWT. + * @return True if tokenEndpointAuthMethod and tokenEndpointAllowReusePvtKeyJwt is NOT in the correct format. + */ + private boolean isInvalidTokenEPReusePvtKeyJwtRequest(String tokenEndpointAuthMethod, + Boolean tokenEndpointAllowReusePvtKeyJwt) { + + if (StringUtils.isNotBlank(tokenEndpointAuthMethod)) { + if (tokenEndpointAuthMethod.equals(PRIVATE_KEY_JWT)) { + return tokenEndpointAllowReusePvtKeyJwt == null; + } + } + return tokenEndpointAllowReusePvtKeyJwt != null; + } + /** * FAPI validation to restrict the token endpoint authentication methods. * Link - https://openid.net/specs/openid-financial-api-part-2-1_0.html#authorization-server (5.2.2 - 14) diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthUtil.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthUtil.java old mode 100644 new mode 100755 index 2fe5e743a0..2ca4524208 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthUtil.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/OAuthUtil.java @@ -37,6 +37,11 @@ import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.base.IdentityConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.configuration.mgt.core.exception.ConfigurationManagementException; +import org.wso2.carbon.identity.configuration.mgt.core.model.Attribute; +import org.wso2.carbon.identity.configuration.mgt.core.model.Resource; +import org.wso2.carbon.identity.core.handler.AbstractIdentityHandler; +import org.wso2.carbon.identity.core.model.IdentityEventListenerConfig; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.cache.OAuthCache; @@ -77,6 +82,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -89,6 +95,12 @@ import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.CURRENT_TOKEN_IDENTIFIER; import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.Config.PRESERVE_LOGGED_IN_SESSION_AT_PASSWORD_UPDATE; import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.ORGANIZATION_LOGIN_HOME_REALM_IDENTIFIER; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.DEFAULT_VALUE_FOR_PREVENT_TOKEN_REUSE; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.ENABLE_TOKEN_REUSE; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.JWT_CONFIGURATION_RESOURCE_NAME; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.JWT_CONFIGURATION_RESOURCE_TYPE_NAME; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.PREVENT_TOKEN_REUSE; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.PVT_KEY_JWT_CLIENT_AUTHENTICATOR_CLASS_NAME; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenBindings.NONE; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.UserType.FEDERATED_USER_DOMAIN_PREFIX; @@ -536,6 +548,7 @@ public static OAuthConsumerAppDTO buildConsumerAppDTO(OAuthAppDO appDO) { .isTokenRevocationWithIDPSessionTerminationEnabled()); dto.setTokenBindingValidationEnabled(appDO.isTokenBindingValidationEnabled()); dto.setTokenEndpointAuthMethod(appDO.getTokenEndpointAuthMethod()); + dto.setTokenEndpointAllowReusePvtKeyJwt(appDO.isTokenEndpointAllowReusePvtKeyJwt()); dto.setTokenEndpointAuthSignatureAlgorithm(appDO.getTokenEndpointAuthSignatureAlgorithm()); dto.setSectorIdentifierURI(appDO.getSectorIdentifierURI()); dto.setIdTokenSignatureAlgorithm(appDO.getIdTokenSignatureAlgorithm()); @@ -1197,4 +1210,73 @@ private static void setOrganizationSSOUserDetails(AuthenticatedUser authenticate authenticatedUser.setFederatedIdPName(orgSsoIdp.getIdentityProviderName()); } } + + /** + * Get the value of the Tenant configuration of Reuse Private key JWT from the tenant configuration. + * + * @param tokenEPAllowReusePvtKeyJwtValue Value of the tokenEPAllowReusePvtKeyJwt configuration. + * @param tokenAuthMethod Token authentication method. + * @return Value of the tokenEPAllowReusePvtKeyJwt configuration. + * @throws IdentityOAuth2ServerException IdentityOAuth2ServerException exception. + */ + public static String getValueOfTokenEPAllowReusePvtKeyJwt(String tokenEPAllowReusePvtKeyJwtValue, + String tokenAuthMethod) + throws IdentityOAuth2ServerException { + + if (tokenEPAllowReusePvtKeyJwtValue == null && StringUtils.isNotBlank(tokenAuthMethod) + && OAuthConstants.PRIVATE_KEY_JWT.equals(tokenAuthMethod)) { + try { + tokenEPAllowReusePvtKeyJwtValue = readTenantConfigurationPvtKeyJWTReuse(); + } catch (ConfigurationManagementException e) { + throw new IdentityOAuth2ServerException("Unable to retrieve JWT Authenticator tenant configuration.", + e); + } + if (tokenEPAllowReusePvtKeyJwtValue == null) { + tokenEPAllowReusePvtKeyJwtValue = readServerConfigurationPvtKeyJWTReuse(); + if (tokenEPAllowReusePvtKeyJwtValue == null) { + tokenEPAllowReusePvtKeyJwtValue = String.valueOf(DEFAULT_VALUE_FOR_PREVENT_TOKEN_REUSE); + } + } + } + return tokenEPAllowReusePvtKeyJwtValue; + } + + private static String readTenantConfigurationPvtKeyJWTReuse() throws ConfigurationManagementException { + + String tokenEPAllowReusePvtKeyJwtTenantConfig = null; + Resource resource = OAuthComponentServiceHolder.getInstance().getConfigurationManager() + .getResource(JWT_CONFIGURATION_RESOURCE_TYPE_NAME, JWT_CONFIGURATION_RESOURCE_NAME); + + if (resource != null) { + tokenEPAllowReusePvtKeyJwtTenantConfig = resource.getAttributes().stream() + .filter(attribute -> ENABLE_TOKEN_REUSE.equals(attribute.getKey())) + .map(Attribute::getValue) + .findFirst() + .orElse(null); + } + return tokenEPAllowReusePvtKeyJwtTenantConfig; + } + + private static String readServerConfigurationPvtKeyJWTReuse() { + + String tokenEPAllowReusePvtKeyJwtTenantConfig = null; + IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty( + AbstractIdentityHandler.class.getName(), PVT_KEY_JWT_CLIENT_AUTHENTICATOR_CLASS_NAME); + + if (identityEventListenerConfig != null + && Boolean.parseBoolean(identityEventListenerConfig.getEnable())) { + if (identityEventListenerConfig.getProperties() != null) { + for (Map.Entry property : identityEventListenerConfig.getProperties().entrySet()) { + String key = (String) property.getKey(); + String value = (String) property.getValue(); + if (Objects.equals(key, PREVENT_TOKEN_REUSE)) { + boolean preventTokenReuse = Boolean.parseBoolean(value); + tokenEPAllowReusePvtKeyJwtTenantConfig = String.valueOf(!preventTokenReuse); + break; + } + } + } + } + return tokenEPAllowReusePvtKeyJwtTenantConfig; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDAO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDAO.java old mode 100644 new mode 100755 index 0608a7a8c9..dfb0bc88f2 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDAO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDAO.java @@ -47,6 +47,7 @@ import org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor; import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2ServerException; import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; @@ -102,6 +103,7 @@ import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_BINDING_TYPE; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_BINDING_TYPE_NONE; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_BINDING_VALIDATION; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_EP_ALLOW_REUSE_PVT_KEY_JWT; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_REVOCATION_WITH_IDP_SESSION_TERMINATION; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCConfigProperties.TOKEN_TYPE; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.OPENID_CONNECT_AUDIENCE; @@ -982,6 +984,10 @@ private void addOrUpdateOIDCSpProperty(OAuthAppDO oauthAppDO, TOKEN_AUTH_METHOD, oauthAppDO.getTokenEndpointAuthMethod(), prepStatementForPropertyAdd, preparedStatementForPropertyUpdate); + addOrUpdateOIDCSpProperty(preprocessedClientId, spTenantId, spOIDCProperties, + TOKEN_EP_ALLOW_REUSE_PVT_KEY_JWT, String.valueOf(oauthAppDO.isTokenEndpointAllowReusePvtKeyJwt()), + prepStatementForPropertyAdd, preparedStatementForPropertyUpdate); + addOrUpdateOIDCSpProperty(preprocessedClientId, spTenantId, spOIDCProperties, TOKEN_AUTH_SIGNATURE_ALGORITHM, oauthAppDO.getTokenEndpointAuthSignatureAlgorithm(), prepStatementForPropertyAdd, preparedStatementForPropertyUpdate); @@ -1636,6 +1642,12 @@ private void addServiceProviderOIDCProperties(Connection connection, addToBatchForOIDCPropertyAdd(processedClientId, spTenantId, prepStmtAddOIDCProperty, TOKEN_AUTH_METHOD, consumerAppDO.getTokenEndpointAuthMethod()); + if (consumerAppDO.isTokenEndpointAllowReusePvtKeyJwt() != null) { + addToBatchForOIDCPropertyAdd(processedClientId, spTenantId, prepStmtAddOIDCProperty, + TOKEN_EP_ALLOW_REUSE_PVT_KEY_JWT, + String.valueOf(consumerAppDO.isTokenEndpointAllowReusePvtKeyJwt())); + } + addToBatchForOIDCPropertyAdd(processedClientId, spTenantId, prepStmtAddOIDCProperty, TOKEN_AUTH_SIGNATURE_ALGORITHM, consumerAppDO.getTokenEndpointAuthSignatureAlgorithm()); @@ -1732,7 +1744,8 @@ private Map> getSpOIDCProperties(Connection connection, return spOIDCProperties; } - private void setSpOIDCProperties(Map> spOIDCProperties, OAuthAppDO oauthApp) { + private void setSpOIDCProperties(Map> spOIDCProperties, OAuthAppDO oauthApp) + throws IdentityOAuth2ServerException { // Handle OIDC audience values if (isOIDCAudienceEnabled() && @@ -1795,6 +1808,11 @@ private void setSpOIDCProperties(Map> spOIDCProperties, OAu if (tokenAuthMethod != null) { oauthApp.setTokenEndpointAuthMethod(tokenAuthMethod); } + String tokenEPAllowReusePvtKeyJwt = OAuthUtil.getValueOfTokenEPAllowReusePvtKeyJwt( + getFirstPropertyValue(spOIDCProperties, TOKEN_EP_ALLOW_REUSE_PVT_KEY_JWT), tokenAuthMethod); + if (tokenEPAllowReusePvtKeyJwt != null) { + oauthApp.setTokenEndpointAllowReusePvtKeyJwt(Boolean.parseBoolean(tokenEPAllowReusePvtKeyJwt)); + } String tokenSignatureAlgorithm = getFirstPropertyValue(spOIDCProperties, TOKEN_AUTH_SIGNATURE_ALGORITHM); if (tokenSignatureAlgorithm != null) { oauthApp.setTokenEndpointAuthSignatureAlgorithm(tokenSignatureAlgorithm); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDO.java index b48b74bae7..50a1f48db4 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dao/OAuthAppDO.java @@ -81,6 +81,7 @@ public class OAuthAppDO extends InboundConfigurationProtocol implements Serializ private boolean tokenRevocationWithIDPSessionTerminationEnabled; private boolean tokenBindingValidationEnabled; private String tokenEndpointAuthMethod; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String tokenEndpointAuthSignatureAlgorithm; private String sectorIdentifierURI; private String idTokenSignatureAlgorithm; @@ -383,6 +384,16 @@ public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + public String getTokenEndpointAuthSignatureAlgorithm() { return tokenEndpointAuthSignatureAlgorithm; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dto/OAuthConsumerAppDTO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dto/OAuthConsumerAppDTO.java index f93e9acc4f..fba94088c1 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dto/OAuthConsumerAppDTO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/dto/OAuthConsumerAppDTO.java @@ -67,6 +67,7 @@ public class OAuthConsumerAppDTO implements InboundProtocolConfigurationDTO { private boolean tokenBindingValidationEnabled; private String tokenEndpointAuthMethod; private String tokenEndpointAuthSignatureAlgorithm; + private Boolean tokenEndpointAllowReusePvtKeyJwt; private String sectorIdentifierURI; private String idTokenSignatureAlgorithm; private String requestObjectSignatureAlgorithm; @@ -384,6 +385,16 @@ public void setTokenEndpointAuthSignatureAlgorithm(String tokenEndpointAuthSigna this.tokenEndpointAuthSignatureAlgorithm = tokenEndpointAuthSignatureAlgorithm; } + public Boolean isTokenEndpointAllowReusePvtKeyJwt() { + + return tokenEndpointAllowReusePvtKeyJwt; + } + + public void setTokenEndpointAllowReusePvtKeyJwt(Boolean tokenEndpointAllowReusePvtKeyJwt) { + + this.tokenEndpointAllowReusePvtKeyJwt = tokenEndpointAllowReusePvtKeyJwt; + } + public String getSectorIdentifierURI() { return sectorIdentifierURI; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java index 5a91f8a515..419c09ac80 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthComponentServiceHolder.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementService; +import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager; import org.wso2.carbon.identity.cors.mgt.core.CORSManagementService; import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl; import org.wso2.carbon.identity.oauth.OauthInboundAuthConfigHandler; @@ -80,6 +81,7 @@ public class OAuthComponentServiceHolder { private AuthorizedAPIManagementService authorizedAPIManagementService; private IdpManager idpManager; private OrganizationUserSharingService organizationUserSharingService; + private ConfigurationManager configurationManager; /** * Get the list of scope validator implementations available. @@ -511,4 +513,24 @@ public void setOrganizationUserSharingService(OrganizationUserSharingService org this.organizationUserSharingService = organizationUserSharingService; } + + /** + * Get the ConfigurationManager instance. + * + * @return ConfigurationManager The ConfigurationManager instance. + */ + public ConfigurationManager getConfigurationManager() { + + return configurationManager; + } + + /** + * Set the ConfigurationManager instance. + * + * @param configurationManager ConfigurationManager The ConfigurationManager instance. + */ + public void setConfigurationManager(ConfigurationManager configurationManager) { + + this.configurationManager = configurationManager; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java index a253d8836d..c0981a6a1c 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java @@ -30,6 +30,7 @@ import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementService; import org.wso2.carbon.identity.application.mgt.inbound.protocol.ApplicationInboundAuthConfigHandler; +import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager; import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent; import org.wso2.carbon.identity.cors.mgt.core.CORSManagementService; import org.wso2.carbon.identity.event.handler.AbstractEventHandler; @@ -521,4 +522,38 @@ protected void unsetOrganizationUserSharingService(OrganizationUserSharingServic } OAuthComponentServiceHolder.getInstance().setOrganizationUserSharingService(null); } + + /** + * Set the ConfigurationManager. + * + * @param configurationManager The {@code ConfigurationManager} instance. + */ + @Reference( + name = "resource.configuration.manager", + service = ConfigurationManager.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unregisterConfigurationManager" + ) + protected void registerConfigurationManager(ConfigurationManager configurationManager) { + + if (log.isDebugEnabled()) { + log.debug("Registering the ConfigurationManager in JWT Client Authenticator ManagementService."); + } + OAuthComponentServiceHolder.getInstance().setConfigurationManager(configurationManager); + } + + + /** + * Unset the ConfigurationManager. + * + * @param configurationManager The {@code ConfigurationManager} instance. + */ + protected void unregisterConfigurationManager(ConfigurationManager configurationManager) { + + if (log.isDebugEnabled()) { + log.debug("Unregistering the ConfigurationManager in JWT Client Authenticator ManagementService."); + } + OAuthComponentServiceHolder.getInstance().setConfigurationManager(null); + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java old mode 100644 new mode 100755 index d295a00267..643bad39f0 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/OAuthAdminServiceImplTest.java @@ -850,6 +850,34 @@ private void testValidateTokenAuthenticationWithInvalidAuthentication() throws E } } + @DataProvider(name = "getTokenAuthMethodAndTokenReuseConfigData") + public Object[][] getTokenAuthMethodAndTokenReuseConfigData() { + + return new Object[][]{ + // Client auth method, Expected result. + {"private_key_jwt", null, true}, + {null, true, true}, + {"", true, true}, + {" ", true, true}, + {"dummy_method", true, true}, + {"private_key_jwt", true, false}, + {null, null, false}, + {"dummy_method", null, false}}; + } + + @Test(description = "Test invalid reuse token config & client auth method combination.", + dataProvider = "getTokenAuthMethodAndTokenReuseConfigData") + private void testInvalidReuseTokenRequestAndClientAuthMethod(String tokenEndpointAuthMethod, + Boolean tokenEndpointAllowReusePvtKeyJwt, + boolean expectedResult) throws Exception { + + OAuthAdminServiceImpl oAuthAdminService = new OAuthAdminServiceImpl(); + + Assert.assertEquals(invokePrivateMethod(oAuthAdminService, + "isInvalidTokenEPReusePvtKeyJwtRequest", new Class[]{String.class, Boolean.class}, + tokenEndpointAuthMethod, tokenEndpointAllowReusePvtKeyJwt), expectedResult); + } + @Test(description = "Test validating signature algorithm") private void testValidateSignatureAlgorithm() throws Exception { @@ -1059,4 +1087,12 @@ private Object invokePrivateMethod(Object object, String methodName, Object... p method.setAccessible(true); return method.invoke(object, params); } + + private Object invokePrivateMethod(Object object, String methodName, Class[] paramTypes, Object... params) + throws Exception { + + Method method = object.getClass().getDeclaredMethod(methodName, paramTypes); + method.setAccessible(true); + return method.invoke(object, params); + } }