diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java index 8304b131479..ae1585531f6 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java @@ -212,6 +212,7 @@ import static org.wso2.carbon.identity.oauth.endpoint.util.EndpointUtil.getSSOConsentService; import static org.wso2.carbon.identity.oauth.endpoint.util.EndpointUtil.retrieveStateForErrorURL; import static org.wso2.carbon.identity.oauth.endpoint.util.EndpointUtil.validateParams; +import static org.wso2.carbon.identity.oauth2.OAuth2Constants.TokenBinderType.CLIENT_REQUEST; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.ACCESS_TOKEN_JS_OBJECT; import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.DYNAMIC_TOKEN_DATA_FUNCTION; import static org.wso2.carbon.identity.openidconnect.model.Constants.AUTH_TIME; @@ -1732,17 +1733,19 @@ private OAuthResponse handleSuccessAuthorization(OAuthMessage oAuthMessage, OIDC String tokenBindingValue = null; if (tokenBinderOptional.isPresent()) { TokenBinder tokenBinder = tokenBinderOptional.get(); - tokenBindingValue = tokenBinder.getOrGenerateTokenBindingValue(oAuthMessage.getRequest()); - tokenBinder.setTokenBindingValueForResponse(oAuthMessage.getResponse(), tokenBindingValue); - if (LoggerUtils.isDiagnosticLogsEnabled()) { - LoggerUtils.triggerDiagnosticLogEvent(new DiagnosticLog.DiagnosticLogBuilder( - OAuthConstants.LogConstants.OAUTH_INBOUND_SERVICE, "generate-token-binding-value") - .inputParam(LogConstants.InputKeys.CLIENT_ID, oauth2Params.getClientId()) - .inputParam("token binding value", tokenBindingValue) - .configParam("token binder type", tokenBinder.getBindingType()) - .resultMessage("Successfully generated token binding value.") - .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION) - .resultStatus(DiagnosticLog.ResultStatus.SUCCESS)); + if (!tokenBinder.getBindingType().equals(CLIENT_REQUEST)) { + tokenBindingValue = tokenBinder.getOrGenerateTokenBindingValue(oAuthMessage.getRequest()); + tokenBinder.setTokenBindingValueForResponse(oAuthMessage.getResponse(), tokenBindingValue); + if (LoggerUtils.isDiagnosticLogsEnabled()) { + LoggerUtils.triggerDiagnosticLogEvent(new DiagnosticLog.DiagnosticLogBuilder( + OAuthConstants.LogConstants.OAUTH_INBOUND_SERVICE, "generate-token-binding-value") + .inputParam(LogConstants.InputKeys.CLIENT_ID, oauth2Params.getClientId()) + .inputParam("token binding value", tokenBindingValue) + .configParam("token binder type", tokenBinder.getBindingType()) + .resultMessage("Successfully generated token binding value.") + .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION) + .resultStatus(DiagnosticLog.ResultStatus.SUCCESS)); + } } } setAuthorizationCode(oAuthMessage, authzRespDTO, builder, tokenBindingValue, oauth2Params, 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 6d25aa307fe..a791045be43 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 @@ -31,6 +31,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 CLIENT_REQUEST = "client-request"; } public static final String GROUPS = "groups"; @@ -42,6 +43,8 @@ public static class TokenBinderType { public static final String OAUTH_CODE_PERSISTENCE_ENABLE = "OAuth.EnableAuthCodePersistence"; public static final String OAUTH_ENABLE_REVOKE_TOKEN_HEADERS = "OAuth.EnableRevokeTokenHeadersInResponse"; + public static final int MAX_ALLOWED_LENGTH = 256; + /** * Constants for global role based scope issuer. */ 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 b3150c8be94..6eb2c0af52d 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 @@ -78,6 +78,7 @@ 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.ClientRequestTokenBinder; 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; @@ -267,6 +268,10 @@ protected void activate(ComponentContext context) { bundleContext.registerService(TokenBinderInfo.class.getName(), certificateBasedTokenBinder, null); } + // Client instance based access token binder. + ClientRequestTokenBinder clientRequestTokenBinder = new ClientRequestTokenBinder(); + bundleContext.registerService(TokenBinderInfo.class.getName(), clientRequestTokenBinder, 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 fda30030a16..fb2543a4b78 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 @@ -97,6 +97,7 @@ import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.GrantTypes.REFRESH_TOKEN; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OauthAppStates.APP_STATE_ACTIVE; +import static org.wso2.carbon.identity.oauth2.OAuth2Constants.MAX_ALLOWED_LENGTH; import static org.wso2.carbon.identity.oauth2.Oauth2ScopeConstants.CONSOLE_SCOPE_PREFIX; import static org.wso2.carbon.identity.oauth2.Oauth2ScopeConstants.INTERNAL_SCOPE_PREFIX; import static org.wso2.carbon.identity.oauth2.Oauth2ScopeConstants.SYSTEM_SCOPE; @@ -1065,11 +1066,22 @@ private void handleTokenBinding(OAuth2AccessTokenReqDTO tokenReqDTO, String gran throw new IdentityOAuth2ClientException(OAuth2ErrorCodes.INVALID_REQUEST, "TLS certificate not found in the request."); } + if (OAuth2Constants.TokenBinderType.CLIENT_REQUEST.equals(tokenBinder.getBindingType())) { + // Treat as 'None' token binding requests. + tokReqMsgCtx.setTokenBinding(null); + return; + } throw new IdentityOAuth2Exception( "Token binding reference cannot be retrieved form the token binder: " + tokenBinder .getBindingType()); } + if (OAuth2Constants.TokenBinderType.CLIENT_REQUEST.equals(tokenBinder.getBindingType()) && + tokenBindingValueOptional.get().length() >= MAX_ALLOWED_LENGTH) { + throw new IdentityOAuth2ClientException(OAuth2ErrorCodes.INVALID_REQUEST, + "Token binding reference length exceeds limit"); + } + String tokenBindingValue = tokenBindingValueOptional.get(); tokReqMsgCtx.setTokenBinding( new TokenBinding(tokenBinder.getBindingType(), OAuth2Util.getTokenBindingReference(tokenBindingValue), diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/ClientRequestTokenBinder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/ClientRequestTokenBinder.java new file mode 100644 index 00000000000..7adf1e4f164 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/bindings/impl/ClientRequestTokenBinder.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022, 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 org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.model.RequestParameter; + +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.CLIENT_REQUEST; + + +/** + * Client Request binding to the token. + */ +public class ClientRequestTokenBinder extends AbstractTokenBinder { + + private static final String CLIENT_INSTANCE_REF = "tokenBindingReference"; + + @Override + public Optional getTokenBindingValue(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO) { + + RequestParameter[] parameters = oAuth2AccessTokenReqDTO.getRequestParameters(); + for (RequestParameter parameter : parameters) { + if (CLIENT_INSTANCE_REF.equals(parameter.getKey()) + && StringUtils.isNotBlank(parameter.getValue()[0])) { + return Optional.ofNullable(parameter.getValue()[0]); + } + } + return Optional.empty(); + } + + @Override + public String getDisplayName() { + + return "Client Request"; + } + + @Override + public String getDescription() { + + return "Client Request Token Binding"; + } + + @Override + public String getBindingType() { + + return CLIENT_REQUEST; + } + + @Override + public List getSupportedGrantTypes() { + Set supportedGrantTypes = OAuthServerConfiguration.getInstance().getSupportedGrantTypes().keySet(); + return supportedGrantTypes.stream().collect(Collectors.toList()); + } + + @Override + public String getOrGenerateTokenBindingValue(HttpServletRequest request) { + + return null; + } + + @Override + public void setTokenBindingValueForResponse(HttpServletResponse response, String bindingValue) { + + } + + @Override + public void clearTokenBindingElements(HttpServletRequest request, HttpServletResponse response) { + + } + + @Override + public boolean isValidTokenBinding(Object request, String bindingReference) { + + return true; + } + + @Override + public boolean isValidTokenBinding(OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO, String bindingReference) { + + return true; + } +}