From bf99abebee311f4c9e1e805f5797e9a0aa20867c Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Thu, 2 Nov 2023 10:54:35 +0530 Subject: [PATCH 1/7] Adding certificate based token binding type --- .../discovery/DiscoveryConstants.java | 7 + .../discovery/OIDProviderConfigResponse.java | 8 + .../builders/ProviderConfigBuilder.java | 3 + .../identity/oauth/common/OAuthConstants.java | 11 + .../introspection/IntrospectionResponse.java | 1 + .../IntrospectionResponseBuilder.java | 15 ++ .../OAuth2IntrospectionEndpoint.java | 10 +- .../IntrospectionResponseBuilderTest.java | 10 +- .../OAuth2IntrospectionEndpointTest.java | 23 +- .../identity/oauth2/OAuth2Constants.java | 1 + .../dto/OAuth2IntrospectionResponseDTO.java | 11 + .../internal/OAuth2ServiceComponent.java | 7 + .../oauth2/token/AccessTokenIssuer.java | 7 + .../identity/oauth2/token/JWTTokenIssuer.java | 12 +- .../impl/CertificateBasedTokenBinder.java | 212 ++++++++++++++++++ .../AbstractAuthorizationGrantHandler.java | 6 + .../handlers/grant/RefreshGrantHandler.java | 6 + .../identity/oauth2/util/OAuth2Util.java | 17 ++ .../validators/TokenValidationHandler.java | 8 +- .../oauth2/token/JWTTokenIssuerTest.java | 18 +- .../identity/oauth2/util/OAuth2UtilTest.java | 27 +++ .../TokenValidationHandlerTest.java | 52 ++++- 22 files changed, 461 insertions(+), 11 deletions(-) create mode 100644 components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java diff --git a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/DiscoveryConstants.java b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/DiscoveryConstants.java index b99e432ff9d..7fb73ec4c16 100644 --- a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/DiscoveryConstants.java +++ b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/DiscoveryConstants.java @@ -363,4 +363,11 @@ public class DiscoveryConstants { * OPTIONAL. URL of the OpenID Connect token discovery endpoint */ public static final String WEBFINGER_ENDPOINT = "WebFinger_endpoint"; + + /** + * tls_client_certificate_bound_access_tokens + * OPTIONAL. Boolean value indicating server support for mutual-TLS client certificate-bound access tokens. + * If omitted, the default value is false. + */ + public static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKEN = "tls_client_certificate_bound_access_tokens"; } diff --git a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/OIDProviderConfigResponse.java b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/OIDProviderConfigResponse.java index e35ce1ab41c..ede548f9960 100644 --- a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/OIDProviderConfigResponse.java +++ b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/OIDProviderConfigResponse.java @@ -80,6 +80,7 @@ public class OIDProviderConfigResponse { private String[] codeChallengeMethodsSupported; private String deviceAuthorizationEndpoint; private String webFingerEndpoint; + private Boolean tlsClientCertificateBoundAccessTokens; public String getIssuer() { return issuer; @@ -508,6 +509,11 @@ public void setWebFingerEndpoint(String webFingerEndpoint) { this.webFingerEndpoint = webFingerEndpoint; } + public void setTlsClientCertificateBoundAccessTokens(Boolean tlsClientCertificateBoundAccessTokens) { + + this.tlsClientCertificateBoundAccessTokens = tlsClientCertificateBoundAccessTokens; + } + public Map<String, Object> getConfigMap() { Map<String, Object> configMap = new HashMap<String, Object>(); configMap.put(DiscoveryConstants.ISSUER.toLowerCase(), this.issuer); @@ -573,6 +579,8 @@ public Map<String, Object> getConfigMap() { configMap.put(DiscoveryConstants.CODE_CHALLENGE_METHODS_SUPPORTED, this.codeChallengeMethodsSupported); configMap.put(DiscoveryConstants.DEVICE_AUTHORIZATION_ENDPOINT, this.deviceAuthorizationEndpoint); configMap.put(DiscoveryConstants.WEBFINGER_ENDPOINT.toLowerCase(), this.webFingerEndpoint); + configMap.put(DiscoveryConstants.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKEN.toLowerCase(), + this.tlsClientCertificateBoundAccessTokens); return configMap; } } diff --git a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java index c5da0dcfb2b..7a52d7b0f19 100644 --- a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java +++ b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java @@ -31,6 +31,7 @@ import org.wso2.carbon.identity.discovery.internal.OIDCDiscoveryDataHolder; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import java.net.URISyntaxException; @@ -145,6 +146,8 @@ public OIDProviderConfigResponse buildOIDProviderConfig(OIDProviderRequest reque providerConfig.setTokenEndpointAuthSigningAlgValuesSupported( supportedTokenEndpointSigningAlgorithms.toArray(new String[0])); providerConfig.setWebFingerEndpoint(OAuth2Util.OAuthURL.getOidcWebFingerEPUrl()); + providerConfig.setTlsClientCertificateBoundAccessTokens(OAuth2Util.getSupportedTokenBindingTypes() + .contains(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER)); return providerConfig; } } 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 868f48f1023..d779ed31296 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 @@ -114,6 +114,17 @@ public final class OAuthConstants { public static final String SECTOR_IDENTIFIER_URI = "sector_identifier_uri"; public static final String SUBJECT_TYPE = "subject_type"; + + public static final String CNF = "cnf"; + public static final String MTLS_AUTH_HEADER = "MutualTLS.ClientCertificateHeader"; + public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; + public static final String END_CERT = "-----END CERTIFICATE-----"; + public static final String JAVAX_SERVLET_REQUEST_CERTIFICATE = "javax.servlet.request.X509Certificate"; + public static final String CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND"; + + public static final String ENABLE_TLS_CERT_TOKEN_BINDING = "OAuth.OpenIDConnect." + + "EnableTLSCertificateBoundAccessTokens"; + /** * Enum for OIDC supported subject types. */ diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponse.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponse.java index e695d2b811c..a485abcb653 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponse.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponse.java @@ -79,6 +79,7 @@ public final class IntrospectionResponse { // OPTIONAL // Access token binding reference. public static final String BINDING_REFERENCE = "binding_ref"; + public static final String CNF = "cnf"; public static final String AUT = "aut"; diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java index bcecc5ec0ed..a25a587836e 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java @@ -22,6 +22,7 @@ import org.json.JSONException; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -242,6 +243,20 @@ public IntrospectionResponseBuilder setBindingReference(String bindingReference) return this; } + /** + * Set cnf value to be bound to the access token. + * + * @param cnfBindingValue Thumbprint of the TLS certificate passed in the request. + * @return IntrospectionResponseBuilder. + */ + public IntrospectionResponseBuilder setCnfBindingValue(String cnfBindingValue) { + + if (StringUtils.isNotBlank(cnfBindingValue)) { + parameters.put(IntrospectionResponse.CNF, Collections.singletonMap("x5t#S256", cnfBindingValue)); + } + return this; + } + /** * @param errorCode Error Code * @return IntrospectionResponseBuilder diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java index cb7ab7a4b56..77ea0f4bad6 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java @@ -25,9 +25,11 @@ import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.handler.AbstractIdentityHandler; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.IntrospectionDataProvider; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.identity.oauth2.dto.OAuth2IntrospectionResponseDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationRequestDTO; @@ -159,8 +161,14 @@ public Response introspect(@FormParam("token") String token, @FormParam("token_t // Provide token binding related info which required to validate the token. if (StringUtils.isNotBlank(introspectionResponse.getBindingType()) && StringUtils .isNotBlank(introspectionResponse.getBindingReference())) { - respBuilder.setBindingType(introspectionResponse.getBindingType()); + String bindingType = introspectionResponse.getBindingType(); + respBuilder.setBindingType(bindingType); respBuilder.setBindingReference(introspectionResponse.getBindingReference()); + if (OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType) && + StringUtils.isNotBlank(introspectionResponse.getCnfBindingValue()) && + Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING))) { + respBuilder.setCnfBindingValue(introspectionResponse.getCnfBindingValue()); + } } // Retrieve list of registered IntrospectionDataProviders. diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java index eb696d6984b..b52409f7b88 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java @@ -17,6 +17,7 @@ */ package org.wso2.carbon.identity.oauth.endpoint.introspection; +import com.fasterxml.jackson.databind.ObjectMapper; import org.json.JSONArray; import org.json.JSONObject; import org.mockito.Mock; @@ -83,7 +84,8 @@ public Object[][] provideBuilderData() { * This method does unit test for build response with values */ @Test(dataProvider = "provideBuilderData") - public void testResposeBuilderWithVal(boolean isActive, int notBefore, int expiration, boolean timesSetInJson) { + public void testResposeBuilderWithVal(boolean isActive, int notBefore, int expiration, boolean timesSetInJson) + throws Exception { String idToken = "eyJhbGciOiJSUzI1NiJ9.eyJhdXRoX3RpbWUiOjE0NTIxNzAxNzYsImV4cCI6MTQ1MjE3Mzc3Niwic3ViI" + "joidXNlQGNhcmJvbi5zdXBlciIsImF6cCI6IjF5TDFfZnpuekdZdXRYNWdCMDNMNnRYR3lqZ2EiLCJhdF9oYXNoI" + @@ -109,6 +111,7 @@ public void testResposeBuilderWithVal(boolean isActive, int notBefore, int expir introspectionResponseBuilder1.setErrorCode("Invalid input"); introspectionResponseBuilder1.setErrorDescription("error_discription"); introspectionResponseBuilder1.setAuthorizedUserType(OAuthConstants.UserType.APPLICATION_USER); + introspectionResponseBuilder1.setCnfBindingValue("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); JSONObject jsonObject = new JSONObject(introspectionResponseBuilder1.build()); @@ -136,6 +139,9 @@ public void testResposeBuilderWithVal(boolean isActive, int notBefore, int expir "ERROR_DESCRIPTION messages are not equal"); assertEquals(jsonObject.get(IntrospectionResponse.AUT), "APPLICATION_USER", "AUT values are not equal"); + Map<String, Object> cnf = new ObjectMapper() + .readValue(jsonObject.get(IntrospectionResponse.CNF).toString(), HashMap.class); + assertEquals(cnf.get("x5t#S256"), "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9", "CNF value is not equal"); } /** @@ -161,6 +167,7 @@ public void testResposeBuilderWithoutVal() { introspectionResponseBuilder2.setErrorCode(""); introspectionResponseBuilder2.setErrorDescription(""); introspectionResponseBuilder2.setAuthorizedUserType(""); + introspectionResponseBuilder2.setCnfBindingValue(""); JSONObject jsonObject2 = new JSONObject(introspectionResponseBuilder2.build()); assertFalse(jsonObject2.has(IntrospectionResponse.EXP), "EXP already exists in the response builder"); @@ -180,6 +187,7 @@ public void testResposeBuilderWithoutVal() { assertFalse(jsonObject2.has(IntrospectionResponse.Error.ERROR_DESCRIPTION), "ERROR_DESCRIPTION already exists in the response builder"); assertFalse(jsonObject2.has(IntrospectionResponse.AUT), "AUT already exists in the response builder"); + assertFalse(jsonObject2.has(IntrospectionResponse.CNF), "CNF value exists in the response builder"); } @Test(dependsOnMethods = "testResposeBuilderWithVal") diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java index 9b2f1e6547c..e72613f4ec7 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java @@ -16,14 +16,18 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.identity.oauth2.dto.OAuth2IntrospectionResponseDTO; import org.wso2.carbon.identity.testutil.powermock.PowerMockIdentityBaseTest; import java.nio.file.Paths; import java.util.HashMap; +import java.util.Map; import javax.ws.rs.core.Response; @@ -37,7 +41,7 @@ import static org.testng.AssertJUnit.assertEquals; @PrepareForTest({PrivilegedCarbonContext.class, LoggerUtils.class, IdentityTenantUtil.class, - OAuthServerConfiguration.class, TokenPersistenceProcessor.class}) + OAuthServerConfiguration.class, TokenPersistenceProcessor.class, IdentityUtil.class}) public class OAuth2IntrospectionEndpointTest extends PowerMockIdentityBaseTest { @Mock @@ -97,14 +101,27 @@ public void testTokenTypeHint(String tokenTypeHint, String expectedTokenType) th mockedIntrospectionResponse.setTokenType(expectedTokenType); when(mockedIntrospectionResponse.getTokenType()).thenReturn(expectedTokenType); + when(mockedIntrospectionResponse.getBindingType()) + .thenReturn(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + when(mockedIntrospectionResponse.getBindingReference()) + .thenReturn("test_reference_value"); + when(mockedIntrospectionResponse.getCnfBindingValue()) + .thenReturn("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); + mockStatic(IdentityUtil.class); + when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); Response response = oAuth2IntrospectionEndpoint.introspect(token, tokenTypeHint, requiredClaims); - HashMap<String, String> map = new Gson().fromJson((String) response.getEntity(), new TypeToken<HashMap<String, - String>>() { + HashMap<String, Object> map = new Gson().fromJson((String) response.getEntity(), new TypeToken<HashMap<String, + Object>>() { }.getType()); assertEquals(map.get("token_type"), expectedTokenType); + assertEquals(map.get("binding_type"), OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + assertEquals(map.get("binding_ref"), "test_reference_value"); + assertEquals(((Map<String, String>) map.get("cnf")).get("x5t#S256"), + "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); + } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Constants.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Constants.java index 0fb8f0294f0..5bc121812db 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Constants.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Constants.java @@ -30,6 +30,7 @@ public static class TokenBinderType { public static final String SSO_SESSION_BASED_TOKEN_BINDER = "sso-session"; public static final String COOKIE_BASED_TOKEN_BINDER = "cookie"; + public static final String CERTIFICATE_BASED_TOKEN_BINDER = "certificate"; } public static final String GROUPS = "groups"; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2IntrospectionResponseDTO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2IntrospectionResponseDTO.java index c83d9adfd9d..114401d10b0 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2IntrospectionResponseDTO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2IntrospectionResponseDTO.java @@ -110,6 +110,7 @@ public class OAuth2IntrospectionResponseDTO { * OPTIONAL. Token binding reference. */ private String bindingReference; + private String cnfBindingValue; /** * OPTIONAL. Authorized user type of the token. (APPLICATION or APPLICATION_USER) @@ -293,6 +294,16 @@ public String getBindingReference() { return bindingReference; } + public void setCnfBindingValue(String cnfBindingValue) { + + this.cnfBindingValue = cnfBindingValue; + } + + public String getCnfBindingValue() { + + return cnfBindingValue; + } + public void setBindingReference(String bindingReference) { this.bindingReference = bindingReference; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java index dae653a6c72..e83b064b237 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java @@ -42,6 +42,7 @@ import org.wso2.carbon.identity.consent.server.configs.mgt.services.ConsentServerConfigsManagementService; import org.wso2.carbon.identity.core.SAMLSSOServiceProviderManager; import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.handler.AbstractEventHandler; import org.wso2.carbon.identity.event.services.IdentityEventService; import org.wso2.carbon.identity.oauth.common.OAuthConstants; @@ -76,6 +77,7 @@ import org.wso2.carbon.identity.oauth2.scopeservice.ScopeMetadataService; import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinder; import org.wso2.carbon.identity.oauth2.token.bindings.handlers.TokenBindingExpiryEventHandler; +import org.wso2.carbon.identity.oauth2.token.bindings.impl.CertificateBasedTokenBinder; import org.wso2.carbon.identity.oauth2.token.bindings.impl.CookieBasedTokenBinder; import org.wso2.carbon.identity.oauth2.token.bindings.impl.DeviceFlowTokenBinder; import org.wso2.carbon.identity.oauth2.token.bindings.impl.SSOSessionBasedTokenBinder; @@ -251,6 +253,11 @@ protected void activate(ComponentContext context) { bundleContext.registerService(TokenBinderInfo.class.getName(), deviceFlowTokenBinder, null); } + if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING))) { + CertificateBasedTokenBinder certificateBasedTokenBinder = new CertificateBasedTokenBinder(); + bundleContext.registerService(TokenBinderInfo.class.getName(), certificateBasedTokenBinder, null); + } + bundleContext.registerService(ResponseTypeRequestValidator.class.getName(), new DeviceFlowResponseTypeRequestValidator(), null); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java index a962d864421..4dfb7bc8886 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java @@ -52,7 +52,9 @@ import org.wso2.carbon.identity.oauth.event.OAuthEventInterceptor; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth2.IDTokenValidationFailureException; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2ClientException; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.ResponseHeader; import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext; import org.wso2.carbon.identity.oauth2.device.cache.DeviceAuthorizationGrantCache; @@ -1058,6 +1060,11 @@ private void handleTokenBinding(OAuth2AccessTokenReqDTO tokenReqDTO, String gran Optional<String> tokenBindingValueOptional = tokenBinder.getTokenBindingValue(tokenReqDTO); if (!tokenBindingValueOptional.isPresent()) { + if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)) && + OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinder.getBindingType())) { + throw new IdentityOAuth2ClientException(OAuth2ErrorCodes.INVALID_REQUEST, + "TLS certificate not found in the request."); + } throw new IdentityOAuth2Exception( "Token binding reference cannot be retrieved form the token binder: " + tokenBinder .getBindingType()); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java index e6d779e0049..f6436979892 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java @@ -60,6 +60,7 @@ import java.text.ParseException; import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -821,8 +822,17 @@ private JWTClaimsSet handleTokenBinding(JWTClaimsSet.Builder jwtClaimsSetBuilder if (tokReqMsgCtx != null && tokReqMsgCtx.getTokenBinding() != null) { // Include token binding into the jwt token. + String bindingType = tokReqMsgCtx.getTokenBinding().getBindingType(); jwtClaimsSetBuilder.claim(TOKEN_BINDING_REF, tokReqMsgCtx.getTokenBinding().getBindingReference()); - jwtClaimsSetBuilder.claim(TOKEN_BINDING_TYPE, tokReqMsgCtx.getTokenBinding().getBindingType()); + jwtClaimsSetBuilder.claim(TOKEN_BINDING_TYPE, bindingType); + if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)) && + OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType)) { + String cnf = tokReqMsgCtx.getTokenBinding().getBindingValue(); + if (StringUtils.isNotBlank(cnf)) { + jwtClaimsSetBuilder.claim(OAuthConstants.CNF, Collections.singletonMap("x5t#S256", + tokReqMsgCtx.getTokenBinding().getBindingValue())); + } + } } return jwtClaimsSetBuilder.build(); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java new file mode 100644 index 00000000000..d07702884e1 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java @@ -0,0 +1,212 @@ +package org.wso2.carbon.identity.oauth2.token.bindings.impl; + +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jose.util.X509CertUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; +import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.oauth.common.OAuthConstants; +import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; +import org.wso2.carbon.identity.oauth.tokenprocessor.HashingPersistenceProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.openidconnect.model.Constants; + +import java.io.ByteArrayInputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.wso2.carbon.identity.oauth2.OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER; + +/** + * This class provides the certificate based token binder implementation. + */ +public class CertificateBasedTokenBinder extends AbstractTokenBinder { + + private static TokenPersistenceProcessor hashingPersistenceProcessor; + private static final String RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN = + "SELECT BINDING.TOKEN_BINDING_TYPE,BINDING.TOKEN_BINDING_VALUE,BINDING.TOKEN_BINDING_REF " + + "FROM IDN_OAUTH2_ACCESS_TOKEN TOKEN LEFT JOIN IDN_OAUTH2_TOKEN_BINDING BINDING ON " + + "TOKEN.TOKEN_ID=BINDING.TOKEN_ID WHERE TOKEN.REFRESH_TOKEN = ? " + + "AND BINDING.TOKEN_BINDING_TYPE = ?"; + + @Override + public String getBindingType() { + + return CERTIFICATE_BASED_TOKEN_BINDER; + } + + @Override + public List<String> getSupportedGrantTypes() { + + Set<String> supportedGrantTypes = OAuthServerConfiguration.getInstance().getSupportedGrantTypes().keySet(); + return supportedGrantTypes.stream().collect(Collectors.toList()); + } + + @Override + public String getDisplayName() { + + return "Certificate Based"; + } + + @Override + public String getDescription() { + + return "Bind the TLS certificate to the token. Supported grant types: Code"; + } + + @Override + public String getOrGenerateTokenBindingValue(HttpServletRequest request) throws OAuthSystemException { + + return null; + } + + @Override + public String getTokenBindingValue(HttpServletRequest request) throws OAuthSystemException { + + String cnfValue = generateCnfHashValue(request); + if (StringUtils.isNotBlank(cnfValue)) { + return cnfValue; + } else { + throw new OAuthSystemException("Error occurred while generating cnf hash value."); + } + } + + @Override + public Optional<String> getTokenBindingValue(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO) { + + return Optional.ofNullable(generateCnfHashValue(oAuth2AccessTokenReqDTO.getHttpServletRequestWrapper())); + } + + @Override + public void setTokenBindingValueForResponse(HttpServletResponse response, String bindingValue) { + + // Not required. + } + + @Override + public void clearTokenBindingElements(HttpServletRequest request, HttpServletResponse response) { + + // Not required. + } + + @Override + public boolean isValidTokenBinding(Object request, String bindingReference) { + + String cnfValue = generateCnfHashValue((HttpServletRequest) request); + if (StringUtils.isNotBlank(cnfValue)) { + return bindingReference.equals(OAuth2Util.getTokenBindingReference(cnfValue)); + } + return false; + } + + @Override + public boolean isValidTokenBinding(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO, String bindingReference) { + + String refreshToken = oAuth2AccessTokenReqDTO.getRefreshToken(); + try { + TokenBinding tokenBinding = getBindingFromRefreshToken(refreshToken, OAuth2Util.isHashEnabled()); + String cnfValue = generateCnfHashValue(oAuth2AccessTokenReqDTO.getHttpServletRequestWrapper()); + + if (tokenBinding != null && CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinding.getBindingType()) && + StringUtils.isNotBlank(cnfValue)) { + return cnfValue.equalsIgnoreCase(tokenBinding.getBindingValue()) && bindingReference + .equalsIgnoreCase(tokenBinding.getBindingReference()); + } + return false; + } catch (IdentityOAuth2Exception e) { + return false; + } + } + + private String generateCnfHashValue(HttpServletRequest request) { + + Base64URL certThumbprint; + X509Certificate certificate = null; + String headerName = Optional.ofNullable(IdentityUtil.getProperty(OAuthConstants.MTLS_AUTH_HEADER)) + .orElse(OAuthConstants.CONFIG_NOT_FOUND); + + String certificateInHeader = request.getHeader(headerName); + Object certObject = Optional.ofNullable(request.getAttribute(OAuthConstants.JAVAX_SERVLET_REQUEST_CERTIFICATE)) + .orElse(null); + + if (StringUtils.isNotBlank(certificateInHeader)) { + try { + certificate = parseCertificate(certificateInHeader); + } catch (CertificateException e) { + return null; + } + } else if (certObject instanceof X509Certificate) { + certificate = (X509Certificate) certObject; + } else if (certObject instanceof X509Certificate[] && ((X509Certificate[]) certObject).length > 0) { + List<X509Certificate> x509Certificates = Arrays.asList((X509Certificate[]) certObject); + certificate = x509Certificates.get(0); + } + + if (certificate != null) { + certThumbprint = X509CertUtils.computeSHA256Thumbprint(certificate); + return certThumbprint.toString(); + } else { + return null; + } + } + + private X509Certificate parseCertificate(String content) throws CertificateException { + + byte[] decodedContent = java.util.Base64.getDecoder().decode(StringUtils.trim(content + .replaceAll(OAuthConstants.BEGIN_CERT, StringUtils.EMPTY) + .replaceAll(OAuthConstants.END_CERT, StringUtils.EMPTY) + )); + + return (X509Certificate) CertificateFactory.getInstance(Constants.X509) + .generateCertificate(new ByteArrayInputStream(decodedContent)); + } + + private TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) + throws IdentityOAuth2Exception { + + hashingPersistenceProcessor = new HashingPersistenceProcessor(); + JdbcTemplate jdbcTemplate = new JdbcTemplate(IdentityDatabaseUtil.getDataSource()); + if (isTokenHashingEnabled) { + refreshToken = hashingPersistenceProcessor.getProcessedRefreshToken(refreshToken); + } + try { + String finalRefreshToken = refreshToken; + List<TokenBinding> tokenBindingList = jdbcTemplate.executeQuery(RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN, + (resultSet, rowNumber) -> { + TokenBinding tokenBinding = new TokenBinding(); + tokenBinding.setBindingType(resultSet.getString(1)); + tokenBinding.setBindingValue(resultSet.getString(2)); + tokenBinding.setBindingReference(resultSet.getString(3)); + + return tokenBinding; + }, + preparedStatement -> { + int parameterIndex = 0; + preparedStatement.setString(++parameterIndex, finalRefreshToken); + preparedStatement.setString(++parameterIndex, CERTIFICATE_BASED_TOKEN_BINDER); + }); + + return tokenBindingList.isEmpty() ? null : tokenBindingList.get(0); + } catch (DataAccessException e) { + String error = String.format("Error obtaining token binding type using refresh token: %s.", + refreshToken); + throw new IdentityOAuth2Exception(error, e); + } + } +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java index 034dd9d0dd2..5e0cf47edc6 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java @@ -44,6 +44,7 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2ClientException; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.OAuth2Service; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; @@ -620,6 +621,11 @@ private String getNewAccessToken(OAuthTokenReqMessageContext tokReqMsgCtx, Oauth } return newAccessToken; } catch (OAuthSystemException e) { + /* Below condition check is added to send a client error when a TLS certificate is not found in the request + in a scenario where it is required to bind the cnf claim to the access token. */ + if (e.getCause() instanceof IdentityOAuth2ClientException) { + throw (IdentityOAuth2ClientException) e.getCause(); + } throw new IdentityOAuth2Exception("Error while generating access token", e); } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java index e6a7b224904..81a91dd552b 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java @@ -38,6 +38,7 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.tokenprocessor.RefreshTokenGrantProcessor; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2ClientException; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.ResponseHeader; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; @@ -486,6 +487,11 @@ private void createTokens(AccessTokenDO accessTokenDO, OAuthTokenReqMessageConte accessTokenDO.setAccessToken(accessToken); accessTokenDO.setRefreshToken(refreshToken); } catch (OAuthSystemException e) { + /* Below condition check is added to send a client error when a TLS certificate is not found in the request + in a scenario where it is required to bind the cnf claim to the access token. */ + if (e.getCause() instanceof IdentityOAuth2ClientException) { + throw (IdentityOAuth2ClientException) e.getCause(); + } throw new IdentityOAuth2Exception("Error when generating the tokens.", e); } catch (InvalidOAuthClientException e) { throw new IdentityOAuth2Exception("Error while retrieving oauth issuer for the app with clientId: " + diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java index 88119789559..74ed55ac5eb 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/OAuth2Util.java @@ -105,6 +105,7 @@ import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.dao.OAuthConsumerDAO; import org.wso2.carbon.identity.oauth.dto.ScopeDTO; +import org.wso2.carbon.identity.oauth.dto.TokenBindingMetaDataDTO; import org.wso2.carbon.identity.oauth.event.OAuthEventInterceptor; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor; @@ -5043,4 +5044,20 @@ public static boolean isTokenPersistenceEnabled() { } return OAuth2Constants.DEFAULT_PERSIST_ENABLED; } + + /** + * Retrieve the list of token binding types supported by the server. + * + * @return Token binding types supported by the server. + */ + public static List<String> getSupportedTokenBindingTypes() { + + List<TokenBindingMetaDataDTO> supportedTokenBindings = OAuthComponentServiceHolder.getInstance() + .getTokenBindingMetaDataDTOs(); + List<String> supportedBindingTypes = new ArrayList<>(); + for (TokenBindingMetaDataDTO tokenBindingMetaDataDTO : supportedTokenBindings) { + supportedBindingTypes.add(tokenBindingMetaDataDTO.getTokenBindingType()); + } + return supportedBindingTypes; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java index e2d6ec775a6..614c01138c9 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.tokenprocessor.TokenProvider; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.authcontext.AuthorizationContextTokenGenerator; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; import org.wso2.carbon.identity.oauth2.dto.OAuth2ClientApplicationDTO; @@ -581,8 +582,13 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation introResp.setClientId(accessTokenDO.getConsumerKey()); // Set token binding info. if (accessTokenDO.getTokenBinding() != null) { - introResp.setBindingType(accessTokenDO.getTokenBinding().getBindingType()); + String bindingType = accessTokenDO.getTokenBinding().getBindingType(); + introResp.setBindingType(bindingType); introResp.setBindingReference(accessTokenDO.getTokenBinding().getBindingReference()); + if (OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType) && + StringUtils.isNotBlank(accessTokenDO.getTokenBinding().getBindingValue())) { + introResp.setCnfBindingValue(accessTokenDO.getTokenBinding().getBindingValue()); + } } // add authorized user type if (tokenType != null) { diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java index 9d632e94bfa..4e70a9e0b8b 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java @@ -37,6 +37,7 @@ import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.common.testng.WithH2Database; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; @@ -47,6 +48,7 @@ import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO; import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; +import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; import org.wso2.carbon.identity.oauth2.token.handlers.claims.JWTAccessTokenClaimProvider; import org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationGrantHandler; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; @@ -93,7 +95,8 @@ OAuth2Util.class, JWTTokenIssuer.class, IdentityTenantUtil.class, - OIDCClaimUtil.class + OIDCClaimUtil.class, + IdentityUtil.class } ) public class JWTTokenIssuerTest extends PowerMockIdentityBaseTest { @@ -179,6 +182,14 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[], authenticatedUser.setUserId(DUMMY_USER_ID); reqMessageContext.setAuthorizedUser(authenticatedUser); + TokenBinding tokenBinding = new TokenBinding(); + tokenBinding.setBindingType(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + tokenBinding.setBindingReference("test_binding_reference"); + tokenBinding.setBindingValue("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); + reqMessageContext.setTokenBinding(tokenBinding); + mockStatic(IdentityUtil.class); + when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); + OAuth2ServiceComponentHolder.getInstance().addJWTAccessTokenClaimProvider( new DummyTestJWTAccessTokenClaimProvider()); OAuth2ServiceComponentHolder.getInstance().addJWTAccessTokenClaimProvider( @@ -196,6 +207,11 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[], "Custom claim injected by the claim provider not found."); assertEquals(plainJWT.getJWTClaimsSet().getClaim(TOKEN_FLOW_CUSTOM_CLAIM), TOKEN_FLOW_CUSTOM_CLAIM_VALUE, "Custom claim value injected by claim provider value mismatch."); + assertEquals(plainJWT.getJWTClaimsSet().getClaim("binding_type"), + OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + assertEquals(plainJWT.getJWTClaimsSet().getClaim("binding_ref"), "test_binding_reference"); + assertEquals(((Map<String, String>) plainJWT.getJWTClaimsSet().getClaim("cnf")).get("x5t#S256"), + "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); } /** diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java index a5cee05e659..1f410f328f6 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/util/OAuth2UtilTest.java @@ -69,11 +69,13 @@ import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.dao.OAuthConsumerDAO; import org.wso2.carbon.identity.oauth.dto.ScopeDTO; +import org.wso2.carbon.identity.oauth.dto.TokenBindingMetaDataDTO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth.tokenprocessor.HashingPersistenceProcessor; 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.OAuth2Constants; import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; import org.wso2.carbon.identity.oauth2.client.authentication.OAuthClientAuthenticator; import org.wso2.carbon.identity.oauth2.client.authentication.OAuthClientAuthnException; @@ -2632,4 +2634,29 @@ public void testGetSupportedClientAuthMethods() { assertTrue(supportedClientAuthMethods.contains("private_key_jwt")); assertEquals(supportedClientAuthMethods.size(), 4); } + + @Test + public void getSupportedTokenBindingTypes() { + + List<TokenBindingMetaDataDTO> tokenBindingMetaDataDTOS = new ArrayList<>(); + TokenBindingMetaDataDTO cookieTokenBindingMetaDataDTO = new TokenBindingMetaDataDTO(); + cookieTokenBindingMetaDataDTO.setTokenBindingType(OAuth2Constants.TokenBinderType.COOKIE_BASED_TOKEN_BINDER); + tokenBindingMetaDataDTOS.add(cookieTokenBindingMetaDataDTO); + TokenBindingMetaDataDTO ssoTokenBindingMetaDataDTO = new TokenBindingMetaDataDTO(); + ssoTokenBindingMetaDataDTO.setTokenBindingType(OAuth2Constants.TokenBinderType.SSO_SESSION_BASED_TOKEN_BINDER); + tokenBindingMetaDataDTOS.add(ssoTokenBindingMetaDataDTO); + TokenBindingMetaDataDTO certificateTokenBindingMetaDataDTO = new TokenBindingMetaDataDTO(); + certificateTokenBindingMetaDataDTO + .setTokenBindingType(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + tokenBindingMetaDataDTOS.add(certificateTokenBindingMetaDataDTO); + when(oAuthComponentServiceHolderMock.getTokenBindingMetaDataDTOs()).thenReturn(tokenBindingMetaDataDTOS); + List<String> supportedTokenBindingTypes = OAuth2Util.getSupportedTokenBindingTypes(); + Assert.assertTrue(supportedTokenBindingTypes + .contains(OAuth2Constants.TokenBinderType.COOKIE_BASED_TOKEN_BINDER)); + Assert.assertTrue(supportedTokenBindingTypes + .contains(OAuth2Constants.TokenBinderType.SSO_SESSION_BASED_TOKEN_BINDER)); + Assert.assertTrue(supportedTokenBindingTypes + .contains(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER)); + Assert.assertEquals(supportedTokenBindingTypes.size(), 3); + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java index 5ef321f0741..3ab78036655 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java @@ -45,12 +45,18 @@ import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.cache.AppInfoCache; +import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; import org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenProvider; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; +import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; +import org.wso2.carbon.identity.oauth2.dao.TokenManagementDAO; import org.wso2.carbon.identity.oauth2.dto.OAuth2ClientApplicationDTO; +import org.wso2.carbon.identity.oauth2.dto.OAuth2IntrospectionResponseDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationRequestDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationResponseDTO; import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; @@ -58,6 +64,7 @@ import org.wso2.carbon.identity.oauth2.token.JWTTokenIssuer; import org.wso2.carbon.identity.oauth2.token.OauthTokenIssuer; import org.wso2.carbon.identity.oauth2.token.OauthTokenIssuerImpl; +import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import org.wso2.carbon.identity.openidconnect.util.TestUtils; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; @@ -81,6 +88,7 @@ import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.support.membermodification.MemberMatcher.method; import static org.powermock.api.support.membermodification.MemberModifier.stub; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; @@ -92,7 +100,8 @@ @PrepareForTest({OAuthServerConfiguration.class, JDBCPersistenceManager.class, IdentityDatabaseUtil.class, IdentityApplicationManagementUtil.class, IdentityProviderManager.class, RealmService.class, LoggerUtils.class, FederatedAuthenticatorConfig.class, OAuth2ServiceComponentHolder.class, OAuth2JWTTokenValidator.class, - OrganizationManagementConfigUtil.class}) + OrganizationManagementConfigUtil.class, IdentityUtil.class, OAuthTokenPersistenceFactory.class, + OAuth2Util.class}) public class TokenValidationHandlerTest extends PowerMockTestCase { private String[] scopeArraySorted = new String[]{"scope1", "scope2", "scope3"}; @@ -137,6 +146,7 @@ public class TokenValidationHandlerTest extends PowerMockTestCase { public void setUp() { authzUser = new AuthenticatedUser(); + authzUser.setAccessingOrganization("test_org"); issuedTime = new Timestamp(System.currentTimeMillis()); refreshTokenIssuedTime = new Timestamp(System.currentTimeMillis()); validityPeriodInMillis = 3600000L; @@ -225,15 +235,51 @@ public void testBuildIntrospectionResponse(boolean isIDPIdColumnEnabled, String authorizationCode); accessTokenDO.setTokenId(accessTokenId); + TokenBinding tokenBinding = new TokenBinding(); + tokenBinding.setBindingType(OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + tokenBinding.setBindingReference("test_binding_reference"); + tokenBinding.setBindingValue("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); + accessTokenDO.setTokenBinding(tokenBinding); + + mockStatic(IdentityUtil.class); + when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain("carbon.super"); + + mockStatic(OAuth2ServiceComponentHolder.class); + OAuth2ServiceComponentHolder oAuth2ServiceComponentHolderInstance = + Mockito.mock(OAuth2ServiceComponentHolder.class); + TokenProvider tokenProvider = Mockito.mock(TokenProvider.class); + when(OAuth2ServiceComponentHolder.getInstance()).thenReturn(oAuth2ServiceComponentHolderInstance); + when(oAuth2ServiceComponentHolderInstance.getTokenProvider()).thenReturn(tokenProvider); + when(tokenProvider.getVerifiedAccessToken(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(accessTokenDO); + + mockStatic(OAuthTokenPersistenceFactory.class); + OAuthTokenPersistenceFactory oAuthTokenPersistenceFactory = Mockito.mock(OAuthTokenPersistenceFactory.class); + TokenManagementDAO tokenManagementDAO = + Mockito.mock(TokenManagementDAO.class); + when(OAuthTokenPersistenceFactory.getInstance()).thenReturn(oAuthTokenPersistenceFactory); + when(oAuthTokenPersistenceFactory.getTokenManagementDAO()).thenReturn(tokenManagementDAO); + when(tokenManagementDAO.getRefreshToken(Mockito.anyString())).thenReturn(accessTokenDO); + OAuthAppDO oAuthAppDO = new OAuthAppDO(); oAuthAppDO.setTokenType("Default"); oAuthAppDO.setApplicationName("testApp"); AppInfoCache appInfoCache = AppInfoCache.getInstance(); appInfoCache.addToCache("testConsumerKey", oAuthAppDO); oAuth2TokenValidationRequestDTO.setAccessToken(accessToken); + mockStatic(OAuth2Util.class); when(OAuth2Util.getPersistenceProcessor()).thenReturn(new PlainTextPersistenceProcessor()); - - assertNotNull(tokenValidationHandler.buildIntrospectionResponse(oAuth2TokenValidationRequestDTO)); + when(OAuth2Util.getAppInformationByAccessTokenDO(Mockito.any())).thenReturn(oAuthAppDO); + when(OAuth2Util.getAccessTokenExpireMillis(Mockito.any(), Mockito.anyBoolean())).thenReturn(1000L); + + OAuth2IntrospectionResponseDTO oAuth2IntrospectionResponseDTO = tokenValidationHandler + .buildIntrospectionResponse(oAuth2TokenValidationRequestDTO); + assertNotNull(oAuth2IntrospectionResponseDTO); + assertEquals(oAuth2IntrospectionResponseDTO.getBindingType(), + OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); + assertEquals(oAuth2IntrospectionResponseDTO.getBindingReference(), "test_binding_reference"); + assertEquals(oAuth2IntrospectionResponseDTO.getCnfBindingValue(), + "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); } private Property getProperty(String name, String value) { From 8ce541fd131416070aac905a7c6af84b919f19ee Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:12:15 +0530 Subject: [PATCH 2/7] Adding pairwise subject type to the wellknown --- .../identity/discovery/builders/ProviderConfigBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java index 7a52d7b0f19..b3df9aaa070 100644 --- a/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java +++ b/components/org.wso2.carbon.identity.discovery/src/main/java/org/wso2/carbon/identity/discovery/builders/ProviderConfigBuilder.java @@ -113,7 +113,7 @@ public OIDProviderConfigResponse buildOIDProviderConfig(OIDProviderRequest reque providerConfig.setResponseTypesSupported(supportedResponseTypeNames.toArray(new String[supportedResponseTypeNames.size()])); - providerConfig.setSubjectTypesSupported(new String[]{"public"}); + providerConfig.setSubjectTypesSupported(new String[]{"public", "pairwise"}); providerConfig.setCheckSessionIframe(buildServiceUrl(IdentityConstants.OAuth.CHECK_SESSION, IdentityUtil.getProperty(IdentityConstants.OAuth.OIDC_CHECK_SESSION_EP_URL))); From 557204e72be2da38c1f206aed9434a465b778ced Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:57:40 +0530 Subject: [PATCH 3/7] Resolving review comments --- .../identity/oauth/common/OAuthConstants.java | 3 +- .../IntrospectionResponseBuilder.java | 4 ++- .../IntrospectionResponseBuilderTest.java | 3 +- .../OAuth2IntrospectionEndpointTest.java | 5 ++- .../internal/OAuth2ServiceComponent.java | 2 ++ .../identity/oauth2/token/JWTTokenIssuer.java | 2 +- .../impl/CertificateBasedTokenBinder.java | 32 +++++++++++++++---- .../AbstractAuthorizationGrantHandler.java | 4 +-- .../handlers/grant/RefreshGrantHandler.java | 4 +-- .../oauth2/token/JWTTokenIssuerTest.java | 4 +-- 10 files changed, 43 insertions(+), 20 deletions(-) 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 6225a1c1387..157817e4f21 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 @@ -121,9 +121,10 @@ public final class OAuthConstants { public static final String END_CERT = "-----END CERTIFICATE-----"; public static final String JAVAX_SERVLET_REQUEST_CERTIFICATE = "javax.servlet.request.X509Certificate"; public static final String CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND"; + public static final String X5T_S256 = "x5t#S256"; public static final String ENABLE_TLS_CERT_TOKEN_BINDING = "OAuth.OpenIDConnect." + - "EnableTLSCertificateBoundAccessTokens"; + "EnableTLSCertificateBoundAccessTokensViaBindingType"; /** * Enum for OIDC supported subject types. diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java index a25a587836e..87484b53863 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilder.java @@ -20,6 +20,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.oltu.oauth2.common.utils.JSONUtils; import org.json.JSONException; +import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import java.util.Collections; @@ -252,7 +253,8 @@ public IntrospectionResponseBuilder setBindingReference(String bindingReference) public IntrospectionResponseBuilder setCnfBindingValue(String cnfBindingValue) { if (StringUtils.isNotBlank(cnfBindingValue)) { - parameters.put(IntrospectionResponse.CNF, Collections.singletonMap("x5t#S256", cnfBindingValue)); + parameters.put(IntrospectionResponse.CNF, + Collections.singletonMap(OAuthConstants.X5T_S256, cnfBindingValue)); } return this; } diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java index b52409f7b88..2896a428eeb 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/IntrospectionResponseBuilderTest.java @@ -141,7 +141,8 @@ public void testResposeBuilderWithVal(boolean isActive, int notBefore, int expir "AUT values are not equal"); Map<String, Object> cnf = new ObjectMapper() .readValue(jsonObject.get(IntrospectionResponse.CNF).toString(), HashMap.class); - assertEquals(cnf.get("x5t#S256"), "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9", "CNF value is not equal"); + assertEquals(cnf.get(OAuthConstants.X5T_S256), "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9", + "CNF value is not equal"); } /** diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java index e72613f4ec7..4b19c573284 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java @@ -119,9 +119,8 @@ public void testTokenTypeHint(String tokenTypeHint, String expectedTokenType) th assertEquals(map.get("token_type"), expectedTokenType); assertEquals(map.get("binding_type"), OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); assertEquals(map.get("binding_ref"), "test_reference_value"); - assertEquals(((Map<String, String>) map.get("cnf")).get("x5t#S256"), - "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); - + assertEquals(((Map<String, String>) map.get(OAuthConstants.CNF)) + .get(OAuthConstants.X5T_S256), "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java index e83b064b237..7933c2d23cd 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java @@ -253,6 +253,8 @@ protected void activate(ComponentContext context) { bundleContext.registerService(TokenBinderInfo.class.getName(), deviceFlowTokenBinder, null); } + /* Certificate based token binder will be enabled only if certificate binding is not being performed in the + MTLS authenticator. By default, the certificate binding type will be enabled. */ if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING))) { CertificateBasedTokenBinder certificateBasedTokenBinder = new CertificateBasedTokenBinder(); bundleContext.registerService(TokenBinderInfo.class.getName(), certificateBasedTokenBinder, null); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java index f6436979892..f1976c4ce2d 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java @@ -829,7 +829,7 @@ private JWTClaimsSet handleTokenBinding(JWTClaimsSet.Builder jwtClaimsSetBuilder OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType)) { String cnf = tokReqMsgCtx.getTokenBinding().getBindingValue(); if (StringUtils.isNotBlank(cnf)) { - jwtClaimsSetBuilder.claim(OAuthConstants.CNF, Collections.singletonMap("x5t#S256", + jwtClaimsSetBuilder.claim(OAuthConstants.CNF, Collections.singletonMap(OAuthConstants.X5T_S256, tokReqMsgCtx.getTokenBinding().getBindingValue())); } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java index d07702884e1..05341480a2e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2023, WSO2 Inc. (http://www.wso2.com). + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.wso2.carbon.identity.oauth2.token.bindings.impl; import com.nimbusds.jose.util.Base64URL; @@ -67,12 +85,13 @@ public String getDisplayName() { @Override public String getDescription() { - return "Bind the TLS certificate to the token. Supported grant types: Code"; + return "Bind the TLS certificate to the token."; } @Override public String getOrGenerateTokenBindingValue(HttpServletRequest request) throws OAuthSystemException { + // Returning null as the TLS certificate cannot be obtained in this flow. return null; } @@ -110,7 +129,7 @@ public boolean isValidTokenBinding(Object request, String bindingReference) { String cnfValue = generateCnfHashValue((HttpServletRequest) request); if (StringUtils.isNotBlank(cnfValue)) { - return bindingReference.equals(OAuth2Util.getTokenBindingReference(cnfValue)); + return StringUtils.equals(bindingReference, OAuth2Util.getTokenBindingReference(cnfValue)); } return false; } @@ -125,8 +144,8 @@ public boolean isValidTokenBinding(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqD if (tokenBinding != null && CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinding.getBindingType()) && StringUtils.isNotBlank(cnfValue)) { - return cnfValue.equalsIgnoreCase(tokenBinding.getBindingValue()) && bindingReference - .equalsIgnoreCase(tokenBinding.getBindingReference()); + return StringUtils.equals(cnfValue, tokenBinding.getBindingValue()) && + StringUtils.equals(bindingReference, tokenBinding.getBindingReference()); } return false; } catch (IdentityOAuth2Exception e) { @@ -197,9 +216,8 @@ private TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isT return tokenBinding; }, preparedStatement -> { - int parameterIndex = 0; - preparedStatement.setString(++parameterIndex, finalRefreshToken); - preparedStatement.setString(++parameterIndex, CERTIFICATE_BASED_TOKEN_BINDER); + preparedStatement.setString(1, finalRefreshToken); + preparedStatement.setString(2, CERTIFICATE_BASED_TOKEN_BINDER); }); return tokenBindingList.isEmpty() ? null : tokenBindingList.get(0); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java index 5e0cf47edc6..e8104d39f2e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java @@ -621,8 +621,8 @@ private String getNewAccessToken(OAuthTokenReqMessageContext tokReqMsgCtx, Oauth } return newAccessToken; } catch (OAuthSystemException e) { - /* Below condition check is added to send a client error when a TLS certificate is not found in the request - in a scenario where it is required to bind the cnf claim to the access token. */ + /* Below condition check is added in order to send a client error when the root cause of the exception is + actually a client error and not an internal server error. */ if (e.getCause() instanceof IdentityOAuth2ClientException) { throw (IdentityOAuth2ClientException) e.getCause(); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java index 81a91dd552b..051b2369984 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java @@ -487,8 +487,8 @@ private void createTokens(AccessTokenDO accessTokenDO, OAuthTokenReqMessageConte accessTokenDO.setAccessToken(accessToken); accessTokenDO.setRefreshToken(refreshToken); } catch (OAuthSystemException e) { - /* Below condition check is added to send a client error when a TLS certificate is not found in the request - in a scenario where it is required to bind the cnf claim to the access token. */ + /* Below condition check is added in order to send a client error when the root cause of the exception is + actually a client error and not an internal server error. */ if (e.getCause() instanceof IdentityOAuth2ClientException) { throw (IdentityOAuth2ClientException) e.getCause(); } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java index 4e70a9e0b8b..fe81431da2e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java @@ -210,8 +210,8 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[], assertEquals(plainJWT.getJWTClaimsSet().getClaim("binding_type"), OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER); assertEquals(plainJWT.getJWTClaimsSet().getClaim("binding_ref"), "test_binding_reference"); - assertEquals(((Map<String, String>) plainJWT.getJWTClaimsSet().getClaim("cnf")).get("x5t#S256"), - "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); + assertEquals(((Map<String, String>) plainJWT.getJWTClaimsSet().getClaim(OAuthConstants.CNF)) + .get(OAuthConstants.X5T_S256), "R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); } /** From 7c2d23e92996381a64c711ff94e8f6730308bb35 Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Mon, 6 Nov 2023 15:50:15 +0530 Subject: [PATCH 4/7] Adding debug logs for exceptions --- .../token/bindings/impl/CertificateBasedTokenBinder.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java index 05341480a2e..9e2826603dd 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java @@ -21,6 +21,8 @@ import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jose.util.X509CertUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; @@ -63,6 +65,8 @@ public class CertificateBasedTokenBinder extends AbstractTokenBinder { "TOKEN.TOKEN_ID=BINDING.TOKEN_ID WHERE TOKEN.REFRESH_TOKEN = ? " + "AND BINDING.TOKEN_BINDING_TYPE = ?"; + private static final Log log = LogFactory.getLog(CertificateBasedTokenBinder.class); + @Override public String getBindingType() { @@ -168,6 +172,9 @@ private String generateCnfHashValue(HttpServletRequest request) { try { certificate = parseCertificate(certificateInHeader); } catch (CertificateException e) { + /* Adding a debug log as these errors cannot be thrown as per the TokenBinder interface implementation. + But null checks have been performed where these methods are being executed. */ + log.debug("Error occurred while extracting the certificate from the request header.", e); return null; } } else if (certObject instanceof X509Certificate) { @@ -181,6 +188,7 @@ private String generateCnfHashValue(HttpServletRequest request) { certThumbprint = X509CertUtils.computeSHA256Thumbprint(certificate); return certThumbprint.toString(); } else { + log.debug("TLS certificate not found in the request."); return null; } } From 1189e9d4d13c525be1351555f1270bf00635f164 Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Mon, 6 Nov 2023 22:43:54 +0530 Subject: [PATCH 5/7] Resolving review comments --- .../identity/oauth/common/OAuthConstants.java | 2 +- .../OAuth2IntrospectionEndpoint.java | 4 +- .../OAuth2IntrospectionEndpointTest.java | 5 +- .../identity/oauth2/dao/SQLQueries.java | 6 +++ .../oauth2/dao/TokenBindingMgtDAO.java | 10 ++++ .../oauth2/dao/TokenBindingMgtDAOImpl.java | 40 ++++++++++++++++ .../internal/OAuth2ServiceComponent.java | 3 +- .../oauth2/token/AccessTokenIssuer.java | 3 +- .../identity/oauth2/token/JWTTokenIssuer.java | 3 +- .../impl/CertificateBasedTokenBinder.java | 48 ++----------------- .../oauth2/token/JWTTokenIssuerTest.java | 6 +-- .../TokenValidationHandlerTest.java | 4 -- 12 files changed, 67 insertions(+), 67 deletions(-) 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 157817e4f21..2628bb78dd5 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 @@ -123,7 +123,7 @@ public final class OAuthConstants { public static final String CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND"; public static final String X5T_S256 = "x5t#S256"; - public static final String ENABLE_TLS_CERT_TOKEN_BINDING = "OAuth.OpenIDConnect." + + public static final String ENABLE_TLS_CERT_BOUND_ACCESS_TOKENS_VIA_BINDING_TYPE = "OAuth.OpenIDConnect." + "EnableTLSCertificateBoundAccessTokensViaBindingType"; /** diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java index 77ea0f4bad6..4dc2fd6bfc4 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpoint.java @@ -25,7 +25,6 @@ import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.handler.AbstractIdentityHandler; -import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.IntrospectionDataProvider; @@ -165,8 +164,7 @@ public Response introspect(@FormParam("token") String token, @FormParam("token_t respBuilder.setBindingType(bindingType); respBuilder.setBindingReference(introspectionResponse.getBindingReference()); if (OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType) && - StringUtils.isNotBlank(introspectionResponse.getCnfBindingValue()) && - Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING))) { + StringUtils.isNotBlank(introspectionResponse.getCnfBindingValue())) { respBuilder.setCnfBindingValue(introspectionResponse.getCnfBindingValue()); } } diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java index 4b19c573284..47a3269aed5 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/introspection/OAuth2IntrospectionEndpointTest.java @@ -16,7 +16,6 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; -import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; @@ -41,7 +40,7 @@ import static org.testng.AssertJUnit.assertEquals; @PrepareForTest({PrivilegedCarbonContext.class, LoggerUtils.class, IdentityTenantUtil.class, - OAuthServerConfiguration.class, TokenPersistenceProcessor.class, IdentityUtil.class}) + OAuthServerConfiguration.class, TokenPersistenceProcessor.class}) public class OAuth2IntrospectionEndpointTest extends PowerMockIdentityBaseTest { @Mock @@ -107,8 +106,6 @@ public void testTokenTypeHint(String tokenTypeHint, String expectedTokenType) th .thenReturn("test_reference_value"); when(mockedIntrospectionResponse.getCnfBindingValue()) .thenReturn("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); - mockStatic(IdentityUtil.class); - when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); Response response = oAuth2IntrospectionEndpoint.introspect(token, tokenTypeHint, requiredClaims); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java index 3030e9c0ce2..ef09d15e4c1 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java @@ -1642,6 +1642,12 @@ public class SQLQueries { public static final String GET_SHARED_APP_ID = "SELECT SHARED_APP_ID FROM SP_SHARED_APP WHERE " + "OWNER_ORG_ID = ? AND MAIN_APP_ID = ? AND SHARED_ORG_ID = ? "; + public static final String RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN = + "SELECT BINDING.TOKEN_BINDING_TYPE,BINDING.TOKEN_BINDING_VALUE,BINDING.TOKEN_BINDING_REF " + + "FROM IDN_OAUTH2_ACCESS_TOKEN TOKEN LEFT JOIN IDN_OAUTH2_TOKEN_BINDING BINDING ON " + + "TOKEN.TOKEN_ID=BINDING.TOKEN_ID WHERE TOKEN.REFRESH_TOKEN = ? " + + "AND BINDING.TOKEN_BINDING_TYPE = ?"; + private SQLQueries() { } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java index 6aeca8dd39b..0c325059b06 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java @@ -76,4 +76,14 @@ default Optional<TokenBinding> getTokenBindingByBindingRef(String tokenId, Strin * @throws IdentityOAuth2Exception in case of failure. */ void deleteTokenBinding(String tokenId) throws IdentityOAuth2Exception; + + /** + * Obtain token binding from refresh token. + * + * @param refreshToken Refresh token sent in the request. + * @param isTokenHashingEnabled Whether token hashing is enabled. + * @throws IdentityOAuth2Exception An exception is thrown if an error occurs while obtaining the token binding. + */ + TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) + throws IdentityOAuth2Exception; } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java index 890f59182cf..f4b31a14a96 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java @@ -20,8 +20,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; +import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.oauth.tokenprocessor.HashingPersistenceProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; @@ -30,9 +34,12 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.List; import java.util.Optional; +import static org.wso2.carbon.identity.oauth2.OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER; import static org.wso2.carbon.identity.oauth2.dao.SQLQueries.DELETE_TOKEN_BINDING_BY_TOKEN_ID; +import static org.wso2.carbon.identity.oauth2.dao.SQLQueries.RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN; import static org.wso2.carbon.identity.oauth2.dao.SQLQueries.RETRIEVE_TOKEN_BINDING_BY_TOKEN_ID; import static org.wso2.carbon.identity.oauth2.dao.SQLQueries.RETRIEVE_TOKEN_BINDING_BY_TOKEN_ID_AND_BINDING_REF; import static org.wso2.carbon.identity.oauth2.dao.SQLQueries.RETRIEVE_TOKEN_BINDING_REF_EXISTS; @@ -163,4 +170,37 @@ public void deleteTokenBinding(String tokenId) throws IdentityOAuth2Exception { throw new IdentityOAuth2Exception("Failed to get token binding for the token id: " + tokenId, e); } } + + @Override + public TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) + throws IdentityOAuth2Exception { + + TokenPersistenceProcessor hashingPersistenceProcessor = new HashingPersistenceProcessor(); + JdbcTemplate jdbcTemplate = new JdbcTemplate(IdentityDatabaseUtil.getDataSource()); + if (isTokenHashingEnabled) { + refreshToken = hashingPersistenceProcessor.getProcessedRefreshToken(refreshToken); + } + try { + String finalRefreshToken = refreshToken; + List<TokenBinding> tokenBindingList = jdbcTemplate.executeQuery(RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN, + (resultSet, rowNumber) -> { + TokenBinding tokenBinding = new TokenBinding(); + tokenBinding.setBindingType(resultSet.getString(1)); + tokenBinding.setBindingValue(resultSet.getString(2)); + tokenBinding.setBindingReference(resultSet.getString(3)); + + return tokenBinding; + }, + preparedStatement -> { + preparedStatement.setString(1, finalRefreshToken); + preparedStatement.setString(2, CERTIFICATE_BASED_TOKEN_BINDER); + }); + + return tokenBindingList.isEmpty() ? null : tokenBindingList.get(0); + } catch (DataAccessException e) { + String error = String.format("Error obtaining token binding type using refresh token: %s.", + refreshToken); + throw new IdentityOAuth2Exception(error, e); + } + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java index 7933c2d23cd..3950a060f80 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponent.java @@ -255,7 +255,8 @@ protected void activate(ComponentContext context) { /* Certificate based token binder will be enabled only if certificate binding is not being performed in the MTLS authenticator. By default, the certificate binding type will be enabled. */ - if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING))) { + if (Boolean.parseBoolean(IdentityUtil + .getProperty(OAuthConstants.ENABLE_TLS_CERT_BOUND_ACCESS_TOKENS_VIA_BINDING_TYPE))) { CertificateBasedTokenBinder certificateBasedTokenBinder = new CertificateBasedTokenBinder(); bundleContext.registerService(TokenBinderInfo.class.getName(), certificateBasedTokenBinder, null); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java index 4dfb7bc8886..770005937a6 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java @@ -1060,8 +1060,7 @@ private void handleTokenBinding(OAuth2AccessTokenReqDTO tokenReqDTO, String gran Optional<String> tokenBindingValueOptional = tokenBinder.getTokenBindingValue(tokenReqDTO); if (!tokenBindingValueOptional.isPresent()) { - if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)) && - OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinder.getBindingType())) { + if (OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinder.getBindingType())) { throw new IdentityOAuth2ClientException(OAuth2ErrorCodes.INVALID_REQUEST, "TLS certificate not found in the request."); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java index f1976c4ce2d..87fe84b5f44 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java @@ -825,8 +825,7 @@ private JWTClaimsSet handleTokenBinding(JWTClaimsSet.Builder jwtClaimsSetBuilder String bindingType = tokReqMsgCtx.getTokenBinding().getBindingType(); jwtClaimsSetBuilder.claim(TOKEN_BINDING_REF, tokReqMsgCtx.getTokenBinding().getBindingReference()); jwtClaimsSetBuilder.claim(TOKEN_BINDING_TYPE, bindingType); - if (Boolean.parseBoolean(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)) && - OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType)) { + if (OAuth2Constants.TokenBinderType.CERTIFICATE_BASED_TOKEN_BINDER.equals(bindingType)) { String cnf = tokReqMsgCtx.getTokenBinding().getBindingValue(); if (StringUtils.isNotBlank(cnf)) { jwtClaimsSetBuilder.claim(OAuthConstants.CNF, Collections.singletonMap(OAuthConstants.X5T_S256, diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java index 9e2826603dd..fcccdd9d1f7 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java @@ -24,15 +24,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import org.wso2.carbon.database.utils.jdbc.JdbcTemplate; -import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; -import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; -import org.wso2.carbon.identity.oauth.tokenprocessor.HashingPersistenceProcessor; -import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; @@ -58,13 +54,6 @@ */ public class CertificateBasedTokenBinder extends AbstractTokenBinder { - private static TokenPersistenceProcessor hashingPersistenceProcessor; - private static final String RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN = - "SELECT BINDING.TOKEN_BINDING_TYPE,BINDING.TOKEN_BINDING_VALUE,BINDING.TOKEN_BINDING_REF " + - "FROM IDN_OAUTH2_ACCESS_TOKEN TOKEN LEFT JOIN IDN_OAUTH2_TOKEN_BINDING BINDING ON " + - "TOKEN.TOKEN_ID=BINDING.TOKEN_ID WHERE TOKEN.REFRESH_TOKEN = ? " + - "AND BINDING.TOKEN_BINDING_TYPE = ?"; - private static final Log log = LogFactory.getLog(CertificateBasedTokenBinder.class); @Override @@ -143,7 +132,8 @@ public boolean isValidTokenBinding(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqD String refreshToken = oAuth2AccessTokenReqDTO.getRefreshToken(); try { - TokenBinding tokenBinding = getBindingFromRefreshToken(refreshToken, OAuth2Util.isHashEnabled()); + TokenBinding tokenBinding = OAuthTokenPersistenceFactory.getInstance().getTokenBindingMgtDAO() + .getBindingFromRefreshToken(refreshToken, OAuth2Util.isHashEnabled()); String cnfValue = generateCnfHashValue(oAuth2AccessTokenReqDTO.getHttpServletRequestWrapper()); if (tokenBinding != null && CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinding.getBindingType()) && @@ -203,36 +193,4 @@ private X509Certificate parseCertificate(String content) throws CertificateExcep return (X509Certificate) CertificateFactory.getInstance(Constants.X509) .generateCertificate(new ByteArrayInputStream(decodedContent)); } - - private TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) - throws IdentityOAuth2Exception { - - hashingPersistenceProcessor = new HashingPersistenceProcessor(); - JdbcTemplate jdbcTemplate = new JdbcTemplate(IdentityDatabaseUtil.getDataSource()); - if (isTokenHashingEnabled) { - refreshToken = hashingPersistenceProcessor.getProcessedRefreshToken(refreshToken); - } - try { - String finalRefreshToken = refreshToken; - List<TokenBinding> tokenBindingList = jdbcTemplate.executeQuery(RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN, - (resultSet, rowNumber) -> { - TokenBinding tokenBinding = new TokenBinding(); - tokenBinding.setBindingType(resultSet.getString(1)); - tokenBinding.setBindingValue(resultSet.getString(2)); - tokenBinding.setBindingReference(resultSet.getString(3)); - - return tokenBinding; - }, - preparedStatement -> { - preparedStatement.setString(1, finalRefreshToken); - preparedStatement.setString(2, CERTIFICATE_BASED_TOKEN_BINDER); - }); - - return tokenBindingList.isEmpty() ? null : tokenBindingList.get(0); - } catch (DataAccessException e) { - String error = String.format("Error obtaining token binding type using refresh token: %s.", - refreshToken); - throw new IdentityOAuth2Exception(error, e); - } - } } diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java index fe81431da2e..0535057f625 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java @@ -37,7 +37,6 @@ import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.common.testng.WithH2Database; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; -import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; @@ -95,8 +94,7 @@ OAuth2Util.class, JWTTokenIssuer.class, IdentityTenantUtil.class, - OIDCClaimUtil.class, - IdentityUtil.class + OIDCClaimUtil.class } ) public class JWTTokenIssuerTest extends PowerMockIdentityBaseTest { @@ -187,8 +185,6 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[], tokenBinding.setBindingReference("test_binding_reference"); tokenBinding.setBindingValue("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); reqMessageContext.setTokenBinding(tokenBinding); - mockStatic(IdentityUtil.class); - when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); OAuth2ServiceComponentHolder.getInstance().addJWTAccessTokenClaimProvider( new DummyTestJWTAccessTokenClaimProvider()); diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java index 3ab78036655..5104307a630 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java @@ -45,7 +45,6 @@ import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.cache.AppInfoCache; -import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; @@ -241,10 +240,7 @@ public void testBuildIntrospectionResponse(boolean isIDPIdColumnEnabled, String tokenBinding.setBindingValue("R4Hj_0nNdIzVvPdCdsWlxNKm6a74cszp4Za4M1iE8P9"); accessTokenDO.setTokenBinding(tokenBinding); - mockStatic(IdentityUtil.class); - when(IdentityUtil.getProperty(OAuthConstants.ENABLE_TLS_CERT_TOKEN_BINDING)).thenReturn("true"); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain("carbon.super"); - mockStatic(OAuth2ServiceComponentHolder.class); OAuth2ServiceComponentHolder oAuth2ServiceComponentHolderInstance = Mockito.mock(OAuth2ServiceComponentHolder.class); From 3da41018b6a5b63ad5435d77db06c498340e3af0 Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:56:23 +0530 Subject: [PATCH 6/7] Resolving review comments --- .../carbon/identity/oauth2/dao/SQLQueries.java | 2 +- .../oauth2/dao/TokenBindingMgtDAO.java | 7 +++++-- .../oauth2/dao/TokenBindingMgtDAOImpl.java | 4 ++-- .../impl/CertificateBasedTokenBinder.java | 18 +++++++++++------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java index ef09d15e4c1..228fa8bb5ca 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/SQLQueries.java @@ -1643,7 +1643,7 @@ public class SQLQueries { "OWNER_ORG_ID = ? AND MAIN_APP_ID = ? AND SHARED_ORG_ID = ? "; public static final String RETRIEVE_TOKEN_BINDING_BY_REFRESH_TOKEN = - "SELECT BINDING.TOKEN_BINDING_TYPE,BINDING.TOKEN_BINDING_VALUE,BINDING.TOKEN_BINDING_REF " + + "SELECT BINDING.TOKEN_BINDING_TYPE, BINDING.TOKEN_BINDING_VALUE, BINDING.TOKEN_BINDING_REF " + "FROM IDN_OAUTH2_ACCESS_TOKEN TOKEN LEFT JOIN IDN_OAUTH2_TOKEN_BINDING BINDING ON " + "TOKEN.TOKEN_ID=BINDING.TOKEN_ID WHERE TOKEN.REFRESH_TOKEN = ? " + "AND BINDING.TOKEN_BINDING_TYPE = ?"; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java index 0c325059b06..08283080183 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java @@ -84,6 +84,9 @@ default Optional<TokenBinding> getTokenBindingByBindingRef(String tokenId, Strin * @param isTokenHashingEnabled Whether token hashing is enabled. * @throws IdentityOAuth2Exception An exception is thrown if an error occurs while obtaining the token binding. */ - TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) - throws IdentityOAuth2Exception; + default Optional<TokenBinding> getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) + throws IdentityOAuth2Exception { + + return Optional.empty(); + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java index f4b31a14a96..4abfc40e093 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAOImpl.java @@ -172,7 +172,7 @@ public void deleteTokenBinding(String tokenId) throws IdentityOAuth2Exception { } @Override - public TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) + public Optional<TokenBinding> getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled) throws IdentityOAuth2Exception { TokenPersistenceProcessor hashingPersistenceProcessor = new HashingPersistenceProcessor(); @@ -196,7 +196,7 @@ public TokenBinding getBindingFromRefreshToken(String refreshToken, boolean isTo preparedStatement.setString(2, CERTIFICATE_BASED_TOKEN_BINDER); }); - return tokenBindingList.isEmpty() ? null : tokenBindingList.get(0); + return tokenBindingList.isEmpty() ? null : Optional.ofNullable(tokenBindingList.get(0)); } catch (DataAccessException e) { String error = String.format("Error obtaining token binding type using refresh token: %s.", refreshToken); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java index fcccdd9d1f7..cbbf5ce6c2e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/CertificateBasedTokenBinder.java @@ -132,14 +132,14 @@ public boolean isValidTokenBinding(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqD String refreshToken = oAuth2AccessTokenReqDTO.getRefreshToken(); try { - TokenBinding tokenBinding = OAuthTokenPersistenceFactory.getInstance().getTokenBindingMgtDAO() + Optional<TokenBinding> tokenBinding = OAuthTokenPersistenceFactory.getInstance().getTokenBindingMgtDAO() .getBindingFromRefreshToken(refreshToken, OAuth2Util.isHashEnabled()); String cnfValue = generateCnfHashValue(oAuth2AccessTokenReqDTO.getHttpServletRequestWrapper()); - if (tokenBinding != null && CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinding.getBindingType()) && - StringUtils.isNotBlank(cnfValue)) { - return StringUtils.equals(cnfValue, tokenBinding.getBindingValue()) && - StringUtils.equals(bindingReference, tokenBinding.getBindingReference()); + if (tokenBinding.isPresent() && CERTIFICATE_BASED_TOKEN_BINDER.equals(tokenBinding.get().getBindingType()) + && StringUtils.isNotBlank(cnfValue)) { + return StringUtils.equals(cnfValue, tokenBinding.get().getBindingValue()) && + StringUtils.equals(bindingReference, tokenBinding.get().getBindingReference()); } return false; } catch (IdentityOAuth2Exception e) { @@ -164,7 +164,9 @@ private String generateCnfHashValue(HttpServletRequest request) { } catch (CertificateException e) { /* Adding a debug log as these errors cannot be thrown as per the TokenBinder interface implementation. But null checks have been performed where these methods are being executed. */ - log.debug("Error occurred while extracting the certificate from the request header.", e); + if (log.isDebugEnabled()) { + log.debug("Error occurred while extracting the certificate from the request header.", e); + } return null; } } else if (certObject instanceof X509Certificate) { @@ -178,7 +180,9 @@ private String generateCnfHashValue(HttpServletRequest request) { certThumbprint = X509CertUtils.computeSHA256Thumbprint(certificate); return certThumbprint.toString(); } else { - log.debug("TLS certificate not found in the request."); + if (log.isDebugEnabled()) { + log.debug("TLS certificate not found in the request."); + } return null; } } From 352953d9b07be7e4c728d94e03332a371ba649b3 Mon Sep 17 00:00:00 2001 From: Chinthaka Jayatilake <37581983+ChinthakaJ98@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:14:46 +0530 Subject: [PATCH 7/7] Updating method description --- .../org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java | 1 + 1 file changed, 1 insertion(+) diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java index 08283080183..3c9e3b516fd 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenBindingMgtDAO.java @@ -82,6 +82,7 @@ default Optional<TokenBinding> getTokenBindingByBindingRef(String tokenId, Strin * * @param refreshToken Refresh token sent in the request. * @param isTokenHashingEnabled Whether token hashing is enabled. + * @return Optional token binding for the refresh token. * @throws IdentityOAuth2Exception An exception is thrown if an error occurs while obtaining the token binding. */ default Optional<TokenBinding> getBindingFromRefreshToken(String refreshToken, boolean isTokenHashingEnabled)