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)