From a9862dba212551cd61ed94540529a8bfaa5bee9d Mon Sep 17 00:00:00 2001 From: dushani Date: Mon, 29 Jan 2024 15:50:29 +0530 Subject: [PATCH] front port fixes from https://github.com/wso2-extensions/identity-inbound-auth-oauth/pull/2091. --- .../DefaultOAuth2RevocationProcessor.java | 71 +++++++ .../DefaultRefreshTokenGrantProcessor.java | 187 ++++++++++++++++++ .../OAuth2RevocationProcessor.java | 81 ++++++++ .../RefreshTokenGrantProcessor.java | 79 ++++++++ .../carbon/identity/oauth2/OAuth2Service.java | 35 ++-- .../dao/OAuthTokenPersistenceFactory.java | 15 +- .../internal/OAuth2ServiceComponent.java | 138 +++++++++++++ .../OAuth2ServiceComponentHolder.java | 96 +++++++++ .../handlers/grant/RefreshGrantHandler.java | 156 ++++----------- .../DefaultOIDCClaimsCallbackHandler.java | 3 + 10 files changed, 723 insertions(+), 138 deletions(-) create mode 100644 components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultOAuth2RevocationProcessor.java create mode 100644 components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultRefreshTokenGrantProcessor.java create mode 100644 components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/OAuth2RevocationProcessor.java create mode 100644 components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/RefreshTokenGrantProcessor.java diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultOAuth2RevocationProcessor.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultOAuth2RevocationProcessor.java new file mode 100644 index 0000000000..822094585b --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultOAuth2RevocationProcessor.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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.oauth.tokenprocessor; + +import org.apache.commons.lang3.StringUtils; +import org.apache.oltu.oauth2.common.message.types.GrantType; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; +import org.wso2.carbon.identity.oauth2.dto.OAuthRevocationRequestDTO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; + +/** + * Handles oauth2 token revocation when persistence layer exists. + */ +public class DefaultOAuth2RevocationProcessor implements OAuth2RevocationProcessor { + + @Override + public void revokeAccessToken(OAuthRevocationRequestDTO revokeRequestDTO, AccessTokenDO accessTokenDO) + throws IdentityOAuth2Exception { + + OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() + .revokeAccessTokens(new String[]{accessTokenDO.getAccessToken()}); + } + + @Override + public void revokeRefreshToken(OAuthRevocationRequestDTO revokeRequestDTO, + RefreshTokenValidationDataDO refreshTokenDO) throws IdentityOAuth2Exception { + + OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() + .revokeAccessTokens(new String[]{refreshTokenDO.getAccessToken()}); + } + + @Override + public RefreshTokenValidationDataDO getRevocableRefreshToken(OAuthRevocationRequestDTO revokeRequestDTO) + throws IdentityOAuth2Exception { + + return OAuthTokenPersistenceFactory.getInstance().getTokenManagementDAO() + .validateRefreshToken(revokeRequestDTO.getConsumerKey(), revokeRequestDTO.getToken()); + } + + @Override + public AccessTokenDO getRevocableAccessToken(OAuthRevocationRequestDTO revokeRequestDTO) + throws IdentityOAuth2Exception { + + return OAuth2Util.findAccessToken(revokeRequestDTO.getToken(), true); + } + + @Override + public boolean isRefreshTokenType(OAuthRevocationRequestDTO revokeRequestDTO) { + + return StringUtils.equals(GrantType.REFRESH_TOKEN.toString(), revokeRequestDTO.getTokenType()); + } +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultRefreshTokenGrantProcessor.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultRefreshTokenGrantProcessor.java new file mode 100644 index 0000000000..f76347c6c8 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultRefreshTokenGrantProcessor.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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.oauth.tokenprocessor; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.base.IdentityConstants; +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.dao.OAuthTokenPersistenceFactory; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * Default implementation of @RefreshTokenProcessor responsible for handling refresh token persistence logic. + */ +public class DefaultRefreshTokenGrantProcessor implements RefreshTokenGrantProcessor { + + private static final Log log = LogFactory.getLog(DefaultRefreshTokenGrantProcessor.class); + public static final String PREV_ACCESS_TOKEN = "previousAccessToken"; + public static final int LAST_ACCESS_TOKEN_RETRIEVAL_LIMIT = 10; + + @Override + public RefreshTokenValidationDataDO validateRefreshToken(OAuthTokenReqMessageContext tokenReqMessageContext) + throws IdentityOAuth2Exception { + + OAuth2AccessTokenReqDTO tokenReq = tokenReqMessageContext.getOauth2AccessTokenReqDTO(); + RefreshTokenValidationDataDO validationBean = OAuthTokenPersistenceFactory.getInstance().getTokenManagementDAO() + .validateRefreshToken(tokenReq.getClientId(), tokenReq.getRefreshToken()); + validatePersistedAccessToken(validationBean, tokenReq.getClientId()); + return validationBean; + } + + @Override + public void persistNewToken(OAuthTokenReqMessageContext tokenReqMessageContext, AccessTokenDO accessTokenBean, + String userStoreDomain, String clientId) throws IdentityOAuth2Exception { + + RefreshTokenValidationDataDO oldAccessToken = + (RefreshTokenValidationDataDO) tokenReqMessageContext.getProperty(PREV_ACCESS_TOKEN); + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { + log.debug(String.format("Previous access token (hashed): %s", DigestUtils.sha256Hex( + oldAccessToken.getAccessToken()))); + } + } + // set the previous access token state to "INACTIVE" and store new access token in single db connection + OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() + .invalidateAndCreateNewAccessToken(oldAccessToken.getTokenId(), + OAuthConstants.TokenStates.TOKEN_STATE_INACTIVE, clientId, + UUID.randomUUID().toString(), accessTokenBean, userStoreDomain, oldAccessToken.getGrantType()); + } + + @Override + public AccessTokenDO createAccessTokenBean(OAuthTokenReqMessageContext tokReqMsgCtx, + OAuth2AccessTokenReqDTO tokenReq, + RefreshTokenValidationDataDO validationBean, String tokenType) + throws IdentityOAuth2Exception { + + Timestamp timestamp = new Timestamp(new Date().getTime()); + String tokenId = UUID.randomUUID().toString(); + + AccessTokenDO accessTokenDO = new AccessTokenDO(); + accessTokenDO.setConsumerKey(tokenReq.getClientId()); + accessTokenDO.setAuthzUser(tokReqMsgCtx.getAuthorizedUser()); + accessTokenDO.setScope(tokReqMsgCtx.getScope()); + accessTokenDO.setTokenType(tokenType); + accessTokenDO.setTokenState(OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE); + accessTokenDO.setTokenId(tokenId); + accessTokenDO.setGrantType(tokenReq.getGrantType()); + accessTokenDO.setIssuedTime(timestamp); + accessTokenDO.setTokenBinding(tokReqMsgCtx.getTokenBinding()); + + if (OAuth2ServiceComponentHolder.isConsentedTokenColumnEnabled()) { + String previousGrantType = validationBean.getGrantType(); + // Check if the previous grant type is consent refresh token type or not. + if (!OAuthConstants.GrantTypes.REFRESH_TOKEN.equals(previousGrantType)) { + // If the previous grant type is not a refresh token, then check if it's a consent token or not. + if (OIDCClaimUtil.isConsentBasedClaimFilteringApplicable(previousGrantType)) { + accessTokenDO.setIsConsentedToken(true); + } + } else { + /* When previousGrantType == refresh_token, we need to check whether the original grant type + is consented or not. */ + AccessTokenDO accessTokenDOFromTokenIdentifier = OAuth2Util.getAccessTokenDOFromTokenIdentifier( + validationBean.getAccessToken(), false); + accessTokenDO.setIsConsentedToken(accessTokenDOFromTokenIdentifier.isConsentedToken()); + } + + if (accessTokenDO.isConsentedToken()) { + tokReqMsgCtx.setConsentedToken(true); + } + } + return accessTokenDO; + } + + private boolean validatePersistedAccessToken(RefreshTokenValidationDataDO validationBean, String clientId) + throws IdentityOAuth2Exception { + + if (validationBean.getAccessToken() == null) { + if (log.isDebugEnabled()) { + log.debug(String.format("Invalid Refresh Token provided for Client with Client Id : %s", clientId)); + } + throw new IdentityOAuth2Exception("Persisted access token data not found"); + } + return true; + } + + @Override + public boolean isLatestRefreshToken(OAuth2AccessTokenReqDTO tokenReq, RefreshTokenValidationDataDO validationBean, + String userStoreDomain) throws IdentityOAuth2Exception { + + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.REFRESH_TOKEN)) { + log.debug(String.format("Evaluating refresh token. Token value(hashed): %s, Token state: %s", + DigestUtils.sha256Hex(tokenReq.getRefreshToken()), validationBean.getRefreshTokenState())); + } else { + log.debug(String.format("Evaluating refresh token. Token state: %s", + validationBean.getRefreshTokenState())); + } + } + if (!OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(validationBean.getRefreshTokenState())) { + /* if refresh token is not in active state, check whether there is an access token issued with the same + * refresh token. + */ + List accessTokenBeans = getAccessTokenBeans(tokenReq, validationBean, userStoreDomain); + for (AccessTokenDO token : accessTokenBeans) { + if (tokenReq.getRefreshToken() != null && tokenReq.getRefreshToken().equals(token.getRefreshToken()) + && (OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(token.getTokenState()) + || OAuthConstants.TokenStates.TOKEN_STATE_EXPIRED.equals(token.getTokenState()))) { + return true; + } + } + if (log.isDebugEnabled()) { + log.debug(String.format("Refresh token: %s is not the latest", tokenReq.getRefreshToken())); + } + return false; + } + return true; + } + + private List getAccessTokenBeans(OAuth2AccessTokenReqDTO tokenReq, + RefreshTokenValidationDataDO validationBean, String userStoreDomain) + throws IdentityOAuth2Exception { + + List accessTokenBeans = OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() + .getLatestAccessTokens(tokenReq.getClientId(), validationBean.getAuthorizedUser(), userStoreDomain, + OAuth2Util.buildScopeString(validationBean.getScope()), + validationBean.getTokenBindingReference(), true, LAST_ACCESS_TOKEN_RETRIEVAL_LIMIT); + if (accessTokenBeans == null || accessTokenBeans.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug(String.format("No previous access tokens found. User: %s, client: %s, scope: %s", + validationBean.getAuthorizedUser(), tokenReq.getClientId(), + OAuth2Util.buildScopeString(validationBean.getScope()))); + } + throw new IdentityOAuth2Exception("No previous access tokens found"); + } + return accessTokenBeans; + } +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/OAuth2RevocationProcessor.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/OAuth2RevocationProcessor.java new file mode 100644 index 0000000000..1bf1901410 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/OAuth2RevocationProcessor.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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.oauth.tokenprocessor; + +import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dto.OAuthRevocationRequestDTO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO; + +/** + * Abstraction layer between OAuth2Service and persistence layer to handle + * revocation logic during token persistence and non-persistence scenarios. + */ +public interface OAuth2RevocationProcessor { + + /** + * Revoke access token. + * + * @param revokeRequestDTO Metadata containing revoke token request. + * @param accessTokenDO {@link AccessTokenDO} instance. + * @throws IdentityOAuth2Exception If an error occurs while revoking the access token. + * @throws UserIdNotFoundException If the user id is not found. + */ + void revokeAccessToken(OAuthRevocationRequestDTO revokeRequestDTO, AccessTokenDO accessTokenDO) + throws IdentityOAuth2Exception, UserIdNotFoundException; + + /** + * Revoke refresh token. + * + * @param revokeRequestDTO Metadata containing revoke token request. + * @param refreshTokenDO {@link RefreshTokenValidationDataDO} instance. + * @throws IdentityOAuth2Exception If an error occurs while revoking the refresh token. + */ + void revokeRefreshToken(OAuthRevocationRequestDTO revokeRequestDTO, + RefreshTokenValidationDataDO refreshTokenDO) throws IdentityOAuth2Exception; + + /** + * Validate and return the refresh token metadata. + * + * @param revokeRequestDTO Metadata containing revoke token request. + * @return RefreshTokenValidationDataDO {@link RefreshTokenValidationDataDO} instance. + * @throws IdentityOAuth2Exception If an error occurs while validating the refresh token. + */ + RefreshTokenValidationDataDO getRevocableRefreshToken(OAuthRevocationRequestDTO revokeRequestDTO) + throws IdentityOAuth2Exception; + + /** + * Validate and return the access token metadata. + * + * @param revokeRequestDTO Metadata containing revoke token request. + * @return AccessTokenDO {@link AccessTokenDO} instance. + * @throws IdentityOAuth2Exception If an error occurs while validating the access token. + */ + AccessTokenDO getRevocableAccessToken(OAuthRevocationRequestDTO revokeRequestDTO) + throws IdentityOAuth2Exception; + + /** + * Check whether revoke request is related to access token or revoke token. + * + * @param revokeRequestDTO Metadata containing revoke token request. + * @return boolean whether it is a refresh token request or not + */ + boolean isRefreshTokenType(OAuthRevocationRequestDTO revokeRequestDTO); +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/RefreshTokenGrantProcessor.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/RefreshTokenGrantProcessor.java new file mode 100644 index 0000000000..36e9a11077 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/RefreshTokenGrantProcessor.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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.oauth.tokenprocessor; + +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.model.AccessTokenDO; +import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; + +/** + * Intermediate processor for handling refresh token persistence logic. + */ +public interface RefreshTokenGrantProcessor { + + /** + * Validate the refresh token. + * + * @param tokenReqMessageContext Token request message context. + * @return Refresh token validation data. + * @throws IdentityOAuth2Exception If an error occurred while validating the refresh token. + */ + RefreshTokenValidationDataDO validateRefreshToken(OAuthTokenReqMessageContext tokenReqMessageContext) + throws IdentityOAuth2Exception; + + /** + * Persist the new access token. + * + * @param tokenReqMessageContext Token request message context. + * @param accessTokenBean Access token data object. + * @param userStoreDomain User store domain. + * @param clientId Client ID. + * @throws IdentityOAuth2Exception If an error occurred while persisting the new access token. + */ + void persistNewToken(OAuthTokenReqMessageContext tokenReqMessageContext, AccessTokenDO accessTokenBean, + String userStoreDomain, String clientId) throws IdentityOAuth2Exception; + + /** + * Create the access token bean. + * + * @param tokReqMsgCtx Token request message context. + * @param tokenReq Token request. + * @param validationBean Refresh token validation data. + * @param tokenType Token type. + * @return Access token data object. + * @throws IdentityOAuth2Exception If an error occurred while creating the access token bean. + */ + AccessTokenDO createAccessTokenBean(OAuthTokenReqMessageContext tokReqMsgCtx, OAuth2AccessTokenReqDTO tokenReq, + RefreshTokenValidationDataDO validationBean, String tokenType) + throws IdentityOAuth2Exception; + + /** + * Check whether the refresh token is the latest refresh token. + * + * @param tokenReq Token request. + * @param validationBean Refresh token validation data. + * @param userStoreDomain User store domain. + * @return True if the refresh token is the latest refresh token. + * @throws IdentityOAuth2Exception If an error occurred while checking whether the refresh token is the latest + */ + boolean isLatestRefreshToken(OAuth2AccessTokenReqDTO tokenReq, RefreshTokenValidationDataDO validationBean, + String userStoreDomain) throws IdentityOAuth2Exception; +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java index e69783fbd4..ca64f69c80 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java @@ -37,6 +37,7 @@ import org.wso2.carbon.identity.oauth.dto.OAuthErrorDTO; import org.wso2.carbon.identity.oauth.event.OAuthEventInterceptor; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; +import org.wso2.carbon.identity.oauth.tokenprocessor.OAuth2RevocationProcessor; import org.wso2.carbon.identity.oauth2.authz.AuthorizationHandlerManager; import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; import org.wso2.carbon.identity.oauth2.authz.validators.DefaultResponseTypeRequestValidator; @@ -515,13 +516,12 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques StringUtils.isNotEmpty(revokeRequestDTO.getToken())) { boolean refreshTokenFirst = false; - if (isRefreshTokenType(revokeRequestDTO)) { + if (getRevocationProcessor().isRefreshTokenType(revokeRequestDTO)) { refreshTokenFirst = true; } if (refreshTokenFirst) { - refreshTokenDO = OAuthTokenPersistenceFactory.getInstance().getTokenManagementDAO() - .validateRefreshToken(revokeRequestDTO.getConsumerKey(), revokeRequestDTO.getToken()); + refreshTokenDO = getRevocationProcessor().getRevocableRefreshToken(revokeRequestDTO); if (refreshTokenDO == null || StringUtils.isEmpty(refreshTokenDO.getRefreshTokenState()) || @@ -530,19 +530,16 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques OAuthConstants.TokenStates.TOKEN_STATE_EXPIRED .equals(refreshTokenDO.getRefreshTokenState()))) { - accessTokenDO = OAuthTokenPersistenceFactory.getInstance() - .getAccessTokenDAO().getAccessToken(revokeRequestDTO.getToken(), true); + accessTokenDO = getRevocationProcessor().getRevocableAccessToken(revokeRequestDTO); refreshTokenDO = null; } } else { - accessTokenDO = OAuth2Util.findAccessToken(revokeRequestDTO.getToken(), true); + accessTokenDO = getRevocationProcessor().getRevocableAccessToken(revokeRequestDTO); if (accessTokenDO == null) { - refreshTokenDO = OAuthTokenPersistenceFactory.getInstance() - .getTokenManagementDAO().validateRefreshToken(revokeRequestDTO.getConsumerKey(), - revokeRequestDTO.getToken()); + refreshTokenDO = getRevocationProcessor().getRevocableRefreshToken(revokeRequestDTO); if (refreshTokenDO == null || StringUtils.isEmpty(refreshTokenDO.getRefreshTokenState()) || @@ -613,9 +610,10 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques OAuthUtil.clearOAuthCache(revokeRequestDTO.getConsumerKey(), refreshTokenDO.getAuthorizedUser(), OAuth2Util.buildScopeString(refreshTokenDO.getScope())); OAuthUtil.clearOAuthCache(revokeRequestDTO.getConsumerKey(), refreshTokenDO.getAuthorizedUser()); - OAuthUtil.clearOAuthCache(refreshTokenDO.getAccessToken()); - OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() - .revokeAccessTokens(new String[] { refreshTokenDO.getAccessToken() }); + if (refreshTokenDO.getAccessToken() != null) { + OAuthUtil.clearOAuthCache(refreshTokenDO.getAccessToken()); + } + getRevocationProcessor().revokeRefreshToken(revokeRequestDTO, refreshTokenDO); addRevokeResponseHeaders(revokeResponseDTO, refreshTokenDO.getAccessToken(), revokeRequestDTO.getToken(), @@ -661,8 +659,7 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques String userId = accessTokenDO.getAuthzUser().getUserId(); synchronized ((revokeRequestDTO.getConsumerKey() + ":" + userId + ":" + scope + ":" + tokenBindingReference).intern()) { - OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() - .revokeAccessTokens(new String[]{accessTokenDO.getAccessToken()}); + getRevocationProcessor().revokeAccessToken(revokeRequestDTO, accessTokenDO); } addRevokeResponseHeaders(revokeResponseDTO, revokeRequestDTO.getToken(), @@ -743,6 +740,16 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques } } + /** + * Get the revocation processor. + * + * @return OAuth2RevocationProcessor + */ + private OAuth2RevocationProcessor getRevocationProcessor() { + + return OAuth2ServiceComponentHolder.getInstance().getRevocationProcessor(); + } + private boolean isRefreshTokenType(OAuthRevocationRequestDTO revokeRequestDTO) { return StringUtils.equals(GrantType.REFRESH_TOKEN.toString(), revokeRequestDTO.getTokenType()); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/OAuthTokenPersistenceFactory.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/OAuthTokenPersistenceFactory.java index 8c01485458..b9e9f2f00e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/OAuthTokenPersistenceFactory.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/OAuthTokenPersistenceFactory.java @@ -20,6 +20,8 @@ package org.wso2.carbon.identity.oauth2.dao; +import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; +import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.openidconnect.dao.CacheBackedScopeClaimMappingDAOImpl; import org.wso2.carbon.identity.openidconnect.dao.RequestObjectDAO; import org.wso2.carbon.identity.openidconnect.dao.RequestObjectDAOImpl; @@ -64,7 +66,11 @@ public AuthorizationCodeDAO getAuthorizationCodeDAO() { public AccessTokenDAO getAccessTokenDAO() { - return tokenDAO; + AccessTokenDAO accessTokenDAO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenDAOService(); + if (accessTokenDAO == null) { + return tokenDAO; + } + return accessTokenDAO; } public OAuthScopeDAO getOAuthScopeDAO() { @@ -74,7 +80,12 @@ public OAuthScopeDAO getOAuthScopeDAO() { public TokenManagementDAO getTokenManagementDAO() { - return managementDAO; + TokenManagementDAO tokenManagementDAO = OAuth2ServiceComponentHolder.getInstance() + .getTokenManagementDAOService(); + if (tokenManagementDAO == null) { + return managementDAO; + } + return tokenManagementDAO; } public RequestObjectDAO getRequestObjectDAO() { 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 85a7be9f3f..fda7095f13 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 @@ -43,6 +43,8 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dto.ScopeDTO; import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder; +import org.wso2.carbon.identity.oauth.tokenprocessor.OAuth2RevocationProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.RefreshTokenGrantProcessor; import org.wso2.carbon.identity.oauth2.OAuth2ScopeService; import org.wso2.carbon.identity.oauth2.OAuth2Service; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; @@ -53,7 +55,9 @@ import org.wso2.carbon.identity.oauth2.client.authentication.OAuthClientAuthenticator; import org.wso2.carbon.identity.oauth2.client.authentication.OAuthClientAuthnService; import org.wso2.carbon.identity.oauth2.client.authentication.PublicClientAuthenticator; +import org.wso2.carbon.identity.oauth2.dao.AccessTokenDAO; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; +import org.wso2.carbon.identity.oauth2.dao.TokenManagementDAO; import org.wso2.carbon.identity.oauth2.device.api.DeviceAuthService; import org.wso2.carbon.identity.oauth2.device.api.DeviceAuthServiceImpl; import org.wso2.carbon.identity.oauth2.device.response.DeviceFlowResponseTypeRequestValidator; @@ -633,6 +637,140 @@ protected void unsetOrganizationUserResidentResolverService( OAuth2ServiceComponentHolder.setOrganizationUserResidentResolverService(null); } + /** + * Sets the refresh token grant processor. + * + * @param refreshTokenGrantProcessor RefreshTokenGrantProcessor + */ + @Reference( + name = "refreshtoken.grant.processor", + service = RefreshTokenGrantProcessor.class, + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetRefreshTokenGrantProcessor" + ) + protected void setRefreshTokenGrantProcessor(RefreshTokenGrantProcessor refreshTokenGrantProcessor) { + + if (log.isDebugEnabled()) { + log.debug("Setting refresh token grant processor."); + } + OAuth2ServiceComponentHolder.getInstance().setRefreshTokenGrantProcessor(refreshTokenGrantProcessor); + } + + /** + * Unsets the refresh token grant processor. + * + * @param refreshTokenGrantProcessor RefreshTokenGrantProcessor + */ + protected void unsetRefreshTokenGrantProcessor(RefreshTokenGrantProcessor refreshTokenGrantProcessor) { + + if (log.isDebugEnabled()) { + log.debug("Unset refresh token grant processor."); + } + OAuth2ServiceComponentHolder.getInstance().setRefreshTokenGrantProcessor(null); + } + + /** + * Sets the access token grant processor. + * + * @param accessTokenDAO AccessTokenDAO + */ + @Reference( + name = "access.token.dao.service", + service = AccessTokenDAO.class, + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetAccessTokenDAOService" + ) + protected void setAccessTokenDAOService(AccessTokenDAO accessTokenDAO) { + + if (log.isDebugEnabled()) { + log.debug(String.format("Adding the Access Token DAO Service : %s", accessTokenDAO.getClass().getName())); + } + OAuth2ServiceComponentHolder.getInstance().setAccessTokenDAOService(accessTokenDAO); + } + + /** + * Unsets the access token grant processor. + * + * @param accessTokenDAO AccessTokenDAO + */ + protected void unsetAccessTokenDAOService(AccessTokenDAO accessTokenDAO) { + + if (log.isDebugEnabled()) { + log.debug(String.format("Removing the Access Token DAO Service : %s", accessTokenDAO.getClass().getName())); + } + OAuth2ServiceComponentHolder.getInstance().setAccessTokenDAOService(null); + } + + /** + * Sets the access token grant processor. + * + * @param tokenMgtDAOService TokenManagementDAO + */ + @Reference( + name = "token.management.dao.service", + service = TokenManagementDAO.class, + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetTokenMgtDAOService" + ) + protected void setTokenMgtDAOService(TokenManagementDAO tokenMgtDAOService) { + + if (log.isDebugEnabled()) { + log.debug(String.format("Adding the Token Mgt DAO Service : %s", tokenMgtDAOService.getClass().getName())); + } + OAuth2ServiceComponentHolder.getInstance().setTokenManagementDAOService(tokenMgtDAOService); + } + + /** + * Unsets the access token grant processor. + * + * @param tokenManagementDAO TokenManagementDAO + */ + protected void unsetTokenMgtDAOService(TokenManagementDAO tokenManagementDAO) { + + if (log.isDebugEnabled()) { + log.debug(String.format("Removing the Token Mgt DAO Service : %s", + tokenManagementDAO.getClass().getName())); + } + OAuth2ServiceComponentHolder.getInstance().setTokenManagementDAOService(null); + } + + + /** + * Sets the access token grant processor. + * + * @param oAuth2RevocationProcessor OAuth2RevocationProcessor + */ + @Reference( + name = "oauth2.revocation.processor", + service = OAuth2RevocationProcessor.class, + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetOAuth2RevocationProcessor" + ) + protected void setOAuth2RevocationProcessor(OAuth2RevocationProcessor oAuth2RevocationProcessor) { + + if (log.isDebugEnabled()) { + log.debug("Setting Oauth2 revocation processor."); + } + OAuth2ServiceComponentHolder.getInstance().setRevocationProcessor(oAuth2RevocationProcessor); + } + + /** + * Unsets the access token grant processor. + * + * @param oAuth2RevocationProcessor OAuth2RevocationProcessor + */ + protected void unsetOAuth2RevocationProcessor(OAuth2RevocationProcessor oAuth2RevocationProcessor) { + + if (log.isDebugEnabled()) { + log.debug("Unset Oauth2 revocation processor."); + } + OAuth2ServiceComponentHolder.setOrganizationUserResidentResolverService(null); + } + private static void loadScopeConfigFile() { List listOIDCScopesClaims = new ArrayList<>(); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java index 2732648b21..99764e4b61 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/internal/OAuth2ServiceComponentHolder.java @@ -25,9 +25,15 @@ import org.wso2.carbon.identity.core.handler.HandlerComparator; import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl; import org.wso2.carbon.identity.oauth.dto.ScopeDTO; +import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultOAuth2RevocationProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultRefreshTokenGrantProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.OAuth2RevocationProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.RefreshTokenGrantProcessor; import org.wso2.carbon.identity.oauth2.authz.validators.ResponseTypeRequestValidator; import org.wso2.carbon.identity.oauth2.bean.Scope; import org.wso2.carbon.identity.oauth2.client.authentication.OAuthClientAuthenticator; +import org.wso2.carbon.identity.oauth2.dao.AccessTokenDAO; +import org.wso2.carbon.identity.oauth2.dao.TokenManagementDAO; import org.wso2.carbon.identity.oauth2.keyidprovider.KeyIDProvider; import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinder; import org.wso2.carbon.identity.openidconnect.ClaimProvider; @@ -70,6 +76,10 @@ public class OAuth2ServiceComponentHolder { private List oidcScopesClaims = new ArrayList<>(); private List oauthScopeBinding = new ArrayList<>(); private ScopeClaimMappingDAO scopeClaimMappingDAO; + private AccessTokenDAO accessTokenDAOService; + private TokenManagementDAO tokenManagementDAOService; + private RefreshTokenGrantProcessor refreshTokenGrantProcessor; + private OAuth2RevocationProcessor revocationProcessor; private OAuth2ServiceComponentHolder() { @@ -379,4 +389,90 @@ public static void setOrganizationUserResidentResolverService( OAuth2ServiceComponentHolder.organizationUserResidentResolverService = organizationUserResidentResolverService; } + + /** + * Get AccessTokenDAO instance. + * + * @return AccessTokenDAO {@link AccessTokenDAO} instance. + */ + public AccessTokenDAO getAccessTokenDAOService() { + + return accessTokenDAOService; + } + + /** + * Set AccessTokenDAO instance. + * + * @param accessTokenDAOService {@link AccessTokenDAO} instance. + */ + public void setAccessTokenDAOService(AccessTokenDAO accessTokenDAOService) { + + this.accessTokenDAOService = accessTokenDAOService; + } + + /** + * Get TokenManagementDAO instance. + * + * @return TokenManagementDAO {@link TokenManagementDAO} instance. + */ + public TokenManagementDAO getTokenManagementDAOService() { + + return tokenManagementDAOService; + } + + /** + * Set TokenManagementDAO instance. + * + * @param tokenManagementDAOService {@link TokenManagementDAO} instance. + */ + public void setTokenManagementDAOService(TokenManagementDAO tokenManagementDAOService) { + + this.tokenManagementDAOService = tokenManagementDAOService; + } + + /** + * Get Refresh Token Grant Processor. + * + * @return RefreshTokenGrantProcessor Refresh Token Grant Processor. + */ + public RefreshTokenGrantProcessor getRefreshTokenGrantProcessor() { + + if (refreshTokenGrantProcessor == null) { + refreshTokenGrantProcessor = new DefaultRefreshTokenGrantProcessor(); + } + return refreshTokenGrantProcessor; + } + + /** + * Set Refresh Token Grant Processor. + * + * @param refreshTokenGrantProcessor Refresh Token Grant Processor. + */ + public void setRefreshTokenGrantProcessor(RefreshTokenGrantProcessor refreshTokenGrantProcessor) { + + this.refreshTokenGrantProcessor = refreshTokenGrantProcessor; + } + + /** + * Get Revocation Processor. + * + * @return Revocation Processor. + */ + public OAuth2RevocationProcessor getRevocationProcessor() { + + if (revocationProcessor == null) { + revocationProcessor = new DefaultOAuth2RevocationProcessor(); + } + return revocationProcessor; + } + + /** + * Set Revocation Processor. + * + * @param revocationProcessor Revocation Processor. + */ + public void setRevocationProcessor(OAuth2RevocationProcessor revocationProcessor) { + + this.revocationProcessor = revocationProcessor; + } } 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 eb95d57c70..398629182c 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 @@ -37,6 +37,7 @@ import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; 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.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.ResponseHeader; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; @@ -83,10 +84,8 @@ public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx) super.validateGrant(tokReqMsgCtx); OAuth2AccessTokenReqDTO tokenReq = tokReqMsgCtx.getOauth2AccessTokenReqDTO(); - RefreshTokenValidationDataDO validationBean = OAuthTokenPersistenceFactory.getInstance() - .getTokenManagementDAO().validateRefreshToken(tokenReq.getClientId(), tokenReq.getRefreshToken()); - - validatePersistedAccessToken(validationBean, tokenReq.getClientId()); + RefreshTokenValidationDataDO validationBean = getRefreshTokenGrantProcessor() + .validateRefreshToken(tokReqMsgCtx); validateRefreshTokenInRequest(tokenReq, validationBean); validateTokenBindingReference(tokenReq, validationBean); @@ -114,7 +113,10 @@ public OAuth2AccessTokenRespDTO issue(OAuthTokenReqMessageContext tokReqMsgCtx) return handleError(OAuth2ErrorCodes.INVALID_GRANT, "Refresh token is expired.", tokenReq); } - AccessTokenDO accessTokenBean = createAccessTokenBean(tokReqMsgCtx, tokenReq, validationBean); + AccessTokenDO accessTokenBean = getRefreshTokenGrantProcessor() + .createAccessTokenBean(tokReqMsgCtx, tokenReq, validationBean, getTokenType()); + // sets accessToken, refreshToken and validity data + setTokenData(accessTokenBean, tokReqMsgCtx, validationBean, tokenReq, accessTokenBean.getIssuedTime()); persistNewToken(tokReqMsgCtx, accessTokenBean, tokenReq.getClientId()); if (log.isDebugEnabled()) { log.debug("Persisted an access token for the refresh token, " + @@ -193,40 +195,13 @@ private boolean validateRefreshTokenInRequest(OAuth2AccessTokenReqDTO tokenReq, throws IdentityOAuth2Exception { validateRefreshTokenStatus(validationBean, tokenReq.getClientId()); - if (isLatestRefreshToken(tokenReq, validationBean)) { + if (getRefreshTokenGrantProcessor().isLatestRefreshToken(tokenReq, + validationBean, getUserStoreDomain(validationBean.getAuthorizedUser()))) { return true; } else { - throw new IdentityOAuth2Exception("Invalid refresh token value in the request"); - } - } - - private boolean isLatestRefreshToken(OAuth2AccessTokenReqDTO tokenReq, - RefreshTokenValidationDataDO validationBean) - throws IdentityOAuth2Exception { - - if (log.isDebugEnabled()) { - log.debug("Evaluating refresh token. Token value: " + tokenReq.getRefreshToken() + ", Token state: " + - validationBean.getRefreshTokenState()); - } - if (!OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(validationBean.getRefreshTokenState())) { - // if refresh token is not in active state, check whether there is an access token - // issued with the same refresh token - List accessTokenBeans = getAccessTokenBeans(tokenReq, validationBean, - getUserStoreDomain(validationBean.getAuthorizedUser())); - for (AccessTokenDO token : accessTokenBeans) { - if (tokenReq.getRefreshToken().equals(token.getRefreshToken()) - && (OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(token.getTokenState()) - || OAuthConstants.TokenStates.TOKEN_STATE_EXPIRED.equals(token.getTokenState()))) { - return true; - } - } - if (log.isDebugEnabled()) { - log.debug("Refresh token: " + tokenReq.getRefreshToken() + " is not the latest"); - } removeIfCached(tokenReq, validationBean); - return false; + throw new IdentityOAuth2Exception("Invalid refresh token value in the request"); } - return true; } private void removeIfCached(OAuth2AccessTokenReqDTO tokenReq, RefreshTokenValidationDataDO validationBean) @@ -247,25 +222,6 @@ private void removeIfCached(OAuth2AccessTokenReqDTO tokenReq, RefreshTokenValida } } - private List getAccessTokenBeans(OAuth2AccessTokenReqDTO tokenReq, - RefreshTokenValidationDataDO validationBean, - String userStoreDomain) throws IdentityOAuth2Exception { - - List accessTokenBeans = OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() - .getLatestAccessTokens(tokenReq.getClientId(), validationBean.getAuthorizedUser(), userStoreDomain, - OAuth2Util.buildScopeString(validationBean.getScope()), - validationBean.getTokenBindingReference(), true, LAST_ACCESS_TOKEN_RETRIEVAL_LIMIT); - if (accessTokenBeans == null || accessTokenBeans.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("No previous access tokens found. User: " + validationBean.getAuthorizedUser() + - ", client: " + tokenReq.getClientId() + ", scope: " + - OAuth2Util.buildScopeString(validationBean.getScope())); - } - throw new IdentityOAuth2Exception("No previous access tokens found"); - } - return accessTokenBeans; - } - private boolean validateRefreshTokenStatus(RefreshTokenValidationDataDO validationBean, String clientId) throws IdentityOAuth2Exception { @@ -281,19 +237,6 @@ private boolean validateRefreshTokenStatus(RefreshTokenValidationDataDO validati return true; } - private boolean validatePersistedAccessToken(RefreshTokenValidationDataDO validationBean, String clientId) - throws IdentityOAuth2Exception { - - if (validationBean.getAccessToken() == null) { - if (log.isDebugEnabled()) { - log.debug("Invalid Refresh Token provided for Client with " + - "Client Id : " + clientId); - } - throw new IdentityOAuth2Exception("Persisted access token data not found"); - } - return true; - } - private OAuth2AccessTokenRespDTO buildTokenResponse(OAuthTokenReqMessageContext tokReqMsgCtx, AccessTokenDO accessTokenBean) { @@ -325,11 +268,8 @@ private void persistNewToken(OAuthTokenReqMessageContext tokReqMsgCtx, AccessTok log.debug("Previous access token (hashed): " + DigestUtils.sha256Hex(oldAccessToken.getAccessToken())); } } - // set the previous access token state to "INACTIVE" and store new access token in single db connection - OAuthTokenPersistenceFactory.getInstance().getAccessTokenDAO() - .invalidateAndCreateNewAccessToken(oldAccessToken.getTokenId(), - OAuthConstants.TokenStates.TOKEN_STATE_INACTIVE, clientId, - UUID.randomUUID().toString(), accessTokenBean, userStoreDomain, oldAccessToken.getGrantType()); + getRefreshTokenGrantProcessor().persistNewToken(tokReqMsgCtx, + accessTokenBean, userStoreDomain, clientId); updateCacheIfEnabled(tokReqMsgCtx, accessTokenBean, clientId, oldAccessToken); } @@ -354,9 +294,11 @@ private void updateCacheIfEnabled(OAuthTokenReqMessageContext tokReqMsgCtx, Acce OAuthCache.getInstance().clearCacheEntry(oauthCacheKey, accessTokenBean.getAuthzUser().getTenantDomain()); // Remove old access token from the AccessTokenCache - OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(oldAccessToken.getAccessToken()); - OAuthCache.getInstance().clearCacheEntry(accessTokenCacheKey, - oldAccessToken.getAuthorizedUser().getTenantDomain()); + if (oldAccessToken.getAccessToken() != null) { + OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(oldAccessToken.getAccessToken()); + OAuthCache.getInstance().clearCacheEntry(accessTokenCacheKey, + oldAccessToken.getAuthorizedUser().getTenantDomain()); + } AccessTokenDO tokenToCache = AccessTokenDO.clone(accessTokenBean); OauthTokenIssuer oauthTokenIssuer; try { @@ -473,8 +415,10 @@ private void clearCache(String clientId, String authorizedUserId, String[] scope OAuthCache.getInstance().clearCacheEntry(oauthCacheKey, tenantDomain); // Remove the old access token from the AccessTokenCache - OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(accessToken); - OAuthCache.getInstance().clearCacheEntry(accessTokenCacheKey, tenantDomain); + if (accessToken != null) { + OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(accessToken); + OAuthCache.getInstance().clearCacheEntry(accessTokenCacheKey, tenantDomain); + } } private boolean isRefreshTokenExpired(RefreshTokenValidationDataDO validationBean) { @@ -556,51 +500,6 @@ private void modifyTokensIfUsernameAssertionEnabled(AccessTokenDO accessTokenDO, } } - private AccessTokenDO createAccessTokenBean(OAuthTokenReqMessageContext tokReqMsgCtx, - OAuth2AccessTokenReqDTO tokenReq, - RefreshTokenValidationDataDO validationBean) - throws IdentityOAuth2Exception { - - Timestamp timestamp = new Timestamp(new Date().getTime()); - String tokenId = UUID.randomUUID().toString(); - - AccessTokenDO accessTokenDO = new AccessTokenDO(); - accessTokenDO.setConsumerKey(tokenReq.getClientId()); - accessTokenDO.setAuthzUser(tokReqMsgCtx.getAuthorizedUser()); - accessTokenDO.setScope(tokReqMsgCtx.getScope()); - accessTokenDO.setTokenType(getTokenType()); - accessTokenDO.setTokenState(OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE); - accessTokenDO.setTokenId(tokenId); - accessTokenDO.setGrantType(tokenReq.getGrantType()); - accessTokenDO.setIssuedTime(timestamp); - accessTokenDO.setTokenBinding(tokReqMsgCtx.getTokenBinding()); - - if (OAuth2ServiceComponentHolder.isConsentedTokenColumnEnabled()) { - String previousGrantType = validationBean.getGrantType(); - // Check if the previous grant type is consent refresh token type or not. - if (!StringUtils.equals(OAuthConstants.GrantTypes.REFRESH_TOKEN, previousGrantType)) { - // If the previous grant type is not a refresh token, then check if it's a consent token or not. - if (OIDCClaimUtil.isConsentBasedClaimFilteringApplicable(previousGrantType)) { - accessTokenDO.setIsConsentedToken(true); - } - } else { - /* When previousGrantType == refresh_token, we need to check whether the original grant type - is consented or not. */ - AccessTokenDO accessTokenDOFromTokenIdentifier = OAuth2Util.getAccessTokenDOFromTokenIdentifier( - validationBean.getAccessToken(), false); - accessTokenDO.setIsConsentedToken(accessTokenDOFromTokenIdentifier.isConsentedToken()); - } - - if (accessTokenDO.isConsentedToken()) { - tokReqMsgCtx.setConsentedToken(true); - } - } - - // sets accessToken, refreshToken and validity data - setTokenData(accessTokenDO, tokReqMsgCtx, validationBean, tokenReq, timestamp); - return accessTokenDO; - } - private long getValidityPeriodInMillis(OAuthTokenReqMessageContext tokReqMsgCtx, OAuthAppDO oAuthAppDO) { long validityPeriodInMillis; @@ -677,6 +576,9 @@ private static void addUserAttributesToCache(AccessTokenDO accessTokenBean, RefreshTokenValidationDataDO oldAccessToken = (RefreshTokenValidationDataDO) msgCtx.getProperty(PREV_ACCESS_TOKEN); + if (oldAccessToken.getAccessToken() == null) { + return; + } AuthorizationGrantCacheKey oldAuthorizationGrantCacheKey = new AuthorizationGrantCacheKey(oldAccessToken .getAccessToken()); if (log.isDebugEnabled()) { @@ -761,4 +663,14 @@ private void validateTokenBindingReference(OAuth2AccessTokenReqDTO tokenReqDTO, throw new IdentityOAuth2Exception("Invalid token binding value is present in the request."); } } + + /** + * Get the RefreshTokenGrantProcessor. + * + * @return RefreshTokenGrantProcessor + */ + private RefreshTokenGrantProcessor getRefreshTokenGrantProcessor() { + + return OAuth2ServiceComponentHolder.getInstance().getRefreshTokenGrantProcessor(); + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultOIDCClaimsCallbackHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultOIDCClaimsCallbackHandler.java index a21217ddd6..f5ad60b3b7 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultOIDCClaimsCallbackHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultOIDCClaimsCallbackHandler.java @@ -686,6 +686,9 @@ private Map getUserClaimsInOidcDialect(Map oidcT */ private boolean isTokenHasCustomUserClaims(RefreshTokenValidationDataDO refreshTokenValidationDataDO) { + if (refreshTokenValidationDataDO.getAccessToken() == null) { + return false; + } AuthorizationGrantCacheKey cacheKey = new AuthorizationGrantCacheKey( refreshTokenValidationDataDO.getAccessToken()); AuthorizationGrantCacheEntry cacheEntry = AuthorizationGrantCache.getInstance()