Skip to content

Commit

Permalink
Merge pull request #2208 from dushaniw/new-claim-token-persist
Browse files Browse the repository at this point in the history
Add proposed changes for token persistence removal
  • Loading branch information
janakamarasena authored Oct 27, 2023
2 parents 37cb1e3 + 559a89f commit 17d460d
Show file tree
Hide file tree
Showing 23 changed files with 310 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public OAuth2TokenValidationResponseDTO validateToken(String accessTokenIdentifi
}

try {
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider()
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedAccessToken(accessTokenIdentifier, false);
} catch (IdentityOAuth2Exception e) {
throw new UserInfoEndpointException("Error in getting AccessTokenDO", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private String getAuthzUserTenantDomain(OAuth2TokenValidationResponseDTO tokenRe

AccessTokenDO accessTokenDO;
try {
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider()
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedAccessToken(tokenResponse.getAuthorizationContextToken().getTokenString(), false);
} catch (IdentityOAuth2Exception e) {
if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public static Map<String, Object> getClaimsFromUserStore(OAuth2TokenValidationRe
String subjectClaimValue = null;

try {
AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider()
AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedAccessToken(tokenResponse.getAuthorizationContextToken().getTokenString(),
false);
userId = accessTokenDO.getAuthzUser().getUserId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth.endpoint.util.ClaimUtil;
import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultAccessTokenProvider;
import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultTokenProvider;
import org.wso2.carbon.identity.oauth2.RequestObjectException;
import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationResponseDTO;
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
Expand Down Expand Up @@ -91,7 +91,7 @@ public class UserInfoJSONResponseBuilderTest extends UserInfoResponseBaseTest {
public void setUpTest() throws Exception {

OAuth2ServiceComponentHolder.getInstance().setScopeClaimMappingDAO(new ScopeClaimMappingDAOImpl());
OAuth2ServiceComponentHolder.getInstance().setAccessTokenProvider(new DefaultAccessTokenProvider());
OAuth2ServiceComponentHolder.getInstance().setTokenProvider(new DefaultTokenProvider());
userInfoJSONResponseBuilder = new UserInfoJSONResponseBuilder();
TestUtils.initiateH2Base();
con = TestUtils.getConnection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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.DefaultAccessTokenProvider;
import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultTokenProvider;
import org.wso2.carbon.identity.oauth.user.UserInfoEndpointException;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationResponseDTO;
Expand Down Expand Up @@ -320,8 +320,8 @@ public void testGetClaimsFromUserStore(boolean mockRealm, boolean mockAccessToke
OAuth2ServiceComponentHolder oAuth2ServiceComponentHolderInstance =
Mockito.mock(OAuth2ServiceComponentHolder.class);
when(OAuth2ServiceComponentHolder.getInstance()).thenReturn(oAuth2ServiceComponentHolderInstance);
when(oAuth2ServiceComponentHolderInstance.getAccessTokenProvider())
.thenReturn(new DefaultAccessTokenProvider());
when(oAuth2ServiceComponentHolderInstance.getTokenProvider())
.thenReturn(new DefaultTokenProvider());
Map<String, Object> claimsMap;
try {
claimsMap = ClaimUtil.getClaimsFromUserStore(mockedValidationTokenResponseDTO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,19 @@

package org.wso2.carbon.identity.oauth.tokenprocessor;

import org.apache.commons.lang.StringUtils;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.wso2.carbon.identity.oauth.OAuthUtil;
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;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;

/**
* Handles oauth2 token revocation when persistence layer exists.
* DefaultOAuth2RevocationProcessor is responsible for handling OAuth2 token revocation
* when a persistence layer is in use. It provides methods to revoke access tokens and
* refresh tokens, as well as a mechanism to revoke tokens associated with a specific user.
*/
public class DefaultOAuth2RevocationProcessor implements OAuth2RevocationProcessor {

Expand All @@ -51,27 +50,6 @@ public void revokeRefreshToken(OAuthRevocationRequestDTO revokeRequestDTO,
.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());
}

@Override
public boolean revokeTokens(String username, UserStoreManager userStoreManager)
throws UserStoreException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,29 @@
package org.wso2.carbon.identity.oauth.tokenprocessor;

import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory;
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;

/**
* Default implementation of AccessTokenProvider for scenarios with token persistence enabled.
* Default implementation of TokenProvider for scenarios with token persistence enabled.
* Verifies access tokens by querying the database, including optional inclusion of expired tokens.
*/
public class DefaultAccessTokenProvider implements AccessTokenProvider {
public class DefaultTokenProvider implements TokenProvider {

@Override
public AccessTokenDO getVerifiedAccessToken(String accessToken, boolean includeExpired)
throws IdentityOAuth2Exception {

return OAuth2Util.findAccessToken(accessToken, includeExpired);
}

@Override
public RefreshTokenValidationDataDO getVerifiedRefreshToken(String refreshToken, String consumerKey)
throws IdentityOAuth2Exception {

return OAuthTokenPersistenceFactory.getInstance().getTokenManagementDAO().validateRefreshToken(consumerKey,
refreshToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import org.wso2.carbon.user.core.UserStoreManager;

/**
* Abstraction layer between OAuth2Service and persistence layer to handle
* revocation logic during token persistence and non-persistence scenarios.
* Abstraction layer between OAuth2Service and persistence layer to handle revocation logic during token persistence
* and non-persistence scenarios.
*/
public interface OAuth2RevocationProcessor {

Expand All @@ -53,43 +53,13 @@ void revokeAccessToken(OAuthRevocationRequestDTO revokeRequestDTO, AccessTokenDO
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
* @throws IdentityOAuth2Exception If an error occurs while checking the token type.
*/
boolean isRefreshTokenType(OAuthRevocationRequestDTO revokeRequestDTO) throws IdentityOAuth2Exception;

/**
* Handle indirect token revocation for internal user events.
*
* @param username User on which the event occurred.
* @param username User on which the event occurred.
* @param userStoreManager User store manager.
* @return true if revocation is successful. Else return false.
* @throws UserStoreException
* @throws UserStoreException If an error occurs while revoking tokens for users.
*/
boolean revokeTokens(String username, UserStoreManager userStoreManager)
throws UserStoreException;
boolean revokeTokens(String username, UserStoreManager userStoreManager) throws UserStoreException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@

import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;

/**
* The AccessTokenProvider interface defines the contract for classes that are responsible
* for verifying and providing access tokens. Implementing classes should offer methods
* to retrieve access tokens based on token data objects, with the option to include expired
* tokens in the verification process and handle potential exceptions.
* The TokenProvider interface defines the contract for classes that are responsible
* for verifying and providing access tokens and refresh tokens. Implementing classes should offer methods
* to retrieve access tokens and refresh token based on token data objects.
*/
public interface AccessTokenProvider {
public interface TokenProvider {

/**
* Retrieves and verifies an access token based on the provided access token data object,
Expand All @@ -43,4 +43,17 @@ public interface AccessTokenProvider {
* @throws IdentityOAuth2Exception If there is an error during the access token retrieval or verification process.
*/
AccessTokenDO getVerifiedAccessToken(String accessToken, boolean includeExpired) throws IdentityOAuth2Exception;


/**
* Retrieves and verifies a refresh token.
*
* @param refreshToken The access token data object to retrieve and verify.
* @param consumerKey Consumer key
* @return The RefreshTokenValidationDataDO if the token is valid (ACTIVE or EXPIRED), or null if the token
* is not found either in ACTIVE or EXPIRED states.
* @throws IdentityOAuth2Exception If there is an error during the access token retrieval or verification process.
*/
RefreshTokenValidationDataDO getVerifiedRefreshToken(String refreshToken, String consumerKey)
throws IdentityOAuth2Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public static class TokenBinderType {

}
public static final String GROUPS = "groups";
public static final String ENTITY_ID = "entity_id";
public static final String IS_CONSENTED = "is_consented";
public static final boolean DEFAULT_PERSIST_ENABLED = true;
public static final String OAUTH_TOKEN_PERSISTENCE_ENABLE = "OAuth.TokenPersistence.Enable";


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,31 +552,29 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques
StringUtils.isNotEmpty(revokeRequestDTO.getToken())) {

boolean refreshTokenFirst = false;
if (getRevocationProcessor().isRefreshTokenType(revokeRequestDTO)) {
if (isRefreshTokenType(revokeRequestDTO)) {
refreshTokenFirst = true;
}

if (refreshTokenFirst) {
refreshTokenDO = getRevocationProcessor().getRevocableRefreshToken(revokeRequestDTO);

refreshTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedRefreshToken(revokeRequestDTO.getToken(), revokeRequestDTO.getConsumerKey());
if (refreshTokenDO == null ||
StringUtils.isEmpty(refreshTokenDO.getRefreshTokenState()) ||
!(OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE
.equals(refreshTokenDO.getRefreshTokenState()) ||
OAuthConstants.TokenStates.TOKEN_STATE_EXPIRED
.equals(refreshTokenDO.getRefreshTokenState()))) {

accessTokenDO = getRevocationProcessor().getRevocableAccessToken(revokeRequestDTO);
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedAccessToken(revokeRequestDTO.getToken(), true);
refreshTokenDO = null;
}

} else {

accessTokenDO = getRevocationProcessor().getRevocableAccessToken(revokeRequestDTO);
accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedAccessToken(revokeRequestDTO.getToken(), true);
if (accessTokenDO == null) {

refreshTokenDO = getRevocationProcessor().getRevocableRefreshToken(revokeRequestDTO);

refreshTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedRefreshToken(revokeRequestDTO.getToken(),
revokeRequestDTO.getConsumerKey());
if (refreshTokenDO == null ||
StringUtils.isEmpty(refreshTokenDO.getRefreshTokenState()) ||
!(OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,11 @@ public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx
synchronized ((consumerKey + ":" + authorizedUserId + ":" + scope).intern()) {

AccessTokenDO existingAccessTokenDO = null;
// check if valid access token exists in cache
if (isHashDisabled && cacheEnabled) {
/*
* Check if valid access token exists in cache. If no token persistence, the token will be not be cached
* against a cache key with userId, scope, client.
*/
if (isHashDisabled && cacheEnabled && OAuth2Util.isTokenPersistenceEnabled()) {
existingAccessTokenDO = (AccessTokenDO) OAuthCache.getInstance().getValueFromCache(cacheKey);
if (existingAccessTokenDO != null) {
if (log.isDebugEnabled()) {
Expand Down Expand Up @@ -242,8 +245,11 @@ public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx
log.debug("Infinite lifetime Access Token found in cache");
}
}

if (cacheEnabled) {
/*
* If no token persistence, the token will be not be cached against a cache key with userId,
* scope, client.
*/
if (cacheEnabled && OAuth2Util.isTokenPersistenceEnabled()) {
OAuthCache.getInstance().addToCache(cacheKey, existingAccessTokenDO);
if (log.isDebugEnabled()) {
log.debug("Access Token was added to cache for cache key : " + cacheKey
Expand Down Expand Up @@ -426,12 +432,21 @@ public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx

// Add the access token to the cache, if cacheEnabled and the hashing oauth key feature turn on.
if (isHashDisabled && cacheEnabled) {
OAuthCache.getInstance().addToCache(cacheKey, newAccessTokenDO);
/*
* If no token persistence, the token will be not be cached against a cache key with userId, scope,
* client. But, token will be cached and managed as an AccessTokenDO against the token identifier.
*/
if (OAuth2Util.isTokenPersistenceEnabled()) {
OAuthCache.getInstance().addToCache(cacheKey, newAccessTokenDO);
if (log.isDebugEnabled()) {
log.debug("Access Token was added to OAuthCache for cache key : "
+ cacheKey.getCacheKeyString());
}
}
// Adding AccessTokenDO to improve validation performance
OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(accessToken);
OAuthCache.getInstance().addToCache(accessTokenCacheKey, newAccessTokenDO);
if (log.isDebugEnabled()) {
log.debug("Access Token was added to OAuthCache for cache key : " + cacheKey.getCacheKeyString());
log.debug("Access Token was added to OAuthCache for cache key : " + accessTokenCacheKey
.getCacheKeyString());
}
Expand Down
Loading

0 comments on commit 17d460d

Please sign in to comment.