diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoISAccessTokenValidator.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoISAccessTokenValidator.java index e9ce263e5b1..c54f13bebb7 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoISAccessTokenValidator.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoISAccessTokenValidator.java @@ -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); diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJWTResponse.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJWTResponse.java index 1d5f7a9fe23..159e42eac0f 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJWTResponse.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJWTResponse.java @@ -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)) { diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtil.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtil.java index 10b39af9b71..67ddb3adc56 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtil.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtil.java @@ -115,7 +115,7 @@ public static Map 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(); diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJSONResponseBuilderTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJSONResponseBuilderTest.java index 1a5768faf4d..7bf88fcc176 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJSONResponseBuilderTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/user/impl/UserInfoJSONResponseBuilderTest.java @@ -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; @@ -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(); diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtilTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtilTest.java index 6fc035a9a80..03f7e8aa7e9 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtilTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/util/ClaimUtilTest.java @@ -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; @@ -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 claimsMap; try { claimsMap = ClaimUtil.getClaimsFromUserStore(mockedValidationTokenResponseDTO); 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 index 583563af909..f928a5d7aac 100644 --- 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 @@ -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 { @@ -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 { diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultAccessTokenProvider.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultTokenProvider.java similarity index 66% rename from components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultAccessTokenProvider.java rename to components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultTokenProvider.java index 45c4ee249d5..64d4036e8e9 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultAccessTokenProvider.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/DefaultTokenProvider.java @@ -19,14 +19,16 @@ 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) @@ -34,4 +36,12 @@ public AccessTokenDO getVerifiedAccessToken(String accessToken, boolean includeE return OAuth2Util.findAccessToken(accessToken, includeExpired); } + + @Override + public RefreshTokenValidationDataDO getVerifiedRefreshToken(String refreshToken, String consumerKey) + throws IdentityOAuth2Exception { + + return OAuthTokenPersistenceFactory.getInstance().getTokenManagementDAO().validateRefreshToken(consumerKey, + refreshToken); + } } 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 index ace006ad616..3db56a78216 100644 --- 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 @@ -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 { @@ -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; } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/AccessTokenProvider.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/TokenProvider.java similarity index 65% rename from components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/AccessTokenProvider.java rename to components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/TokenProvider.java index 35021787876..59d0354a963 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/AccessTokenProvider.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/tokenprocessor/TokenProvider.java @@ -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, @@ -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; } 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 a5282962344..0fb8f0294f0 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 @@ -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"; /** 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 121ed89f59c..94cacdcf9dc 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 @@ -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 diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java index ec30bb310be..b68ff533ce8 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java @@ -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()) { @@ -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 @@ -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()); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java index db985b7e872..35e616eccdc 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java @@ -506,8 +506,11 @@ private static AccessTokenDO getExistingToken(OAuthAuthzReqMessageContext oauthA String scope = OAuth2Util.buildScopeString(oauthAuthzMsgCtx.getApprovedScope()); String consumerKey = authorizationReqDTO.getConsumerKey(); String authenticatedIDP = OAuth2Util.getAuthenticatedIDP(authorizationReqDTO.getUser()); - - if (cacheEnabled) { + /* + * If no token persistence, the token is not cached against a cache key with userId, scope, client and + * idp since multiple active tokens can exist for the same key. + */ + if (cacheEnabled && OAuth2Util.isTokenPersistenceEnabled()) { existingTokenBean = getExistingTokenFromCache(consumerKey, scope, authorizedUserId, authenticatedIDP); } @@ -975,7 +978,13 @@ private static OAuthCacheKey getOAuthCacheKey(String consumerKey, String scope, private static void addTokenToCache(OAuthCacheKey cacheKey, AccessTokenDO tokenBean) { - OAuthCache.getInstance().addToCache(cacheKey, tokenBean); + /* + * If no token persistence, the token will be not be cached against a cache key with userId, scope, client and + * idp. But, token will be cached and managed as an AccessTokenDO against the token identifier. + */ + if (OAuth2Util.isTokenPersistenceEnabled()) { + OAuthCache.getInstance().addToCache(cacheKey, tokenBean); + } // Adding AccessTokenDO to improve validation performance OAuthCacheKey accessTokenCacheKey = new OAuthCacheKey(tokenBean.getAccessToken()); OAuthCache.getInstance().addToCache(accessTokenCacheKey, tokenBean); 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 fc3d00770ef..dae653a6c72 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 @@ -49,9 +49,9 @@ 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.AccessTokenProvider; import org.wso2.carbon.identity.oauth.tokenprocessor.OAuth2RevocationProcessor; import org.wso2.carbon.identity.oauth.tokenprocessor.RefreshTokenGrantProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenProvider; import org.wso2.carbon.identity.oauth2.OAuth2ScopeService; import org.wso2.carbon.identity.oauth2.OAuth2Service; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; @@ -743,36 +743,36 @@ protected void unsetOrganizationUserResidentResolverService( } /** - * Sets the access token provider. + * Sets the token provider. * - * @param accessTokenProvider AccessTokenProvider + * @param tokenProvider TokenProvider */ @Reference( - name = "access.token.provider", - service = AccessTokenProvider.class, + name = "token.provider", + service = TokenProvider.class, cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, - unbind = "unsetAccessTokenProvider" + unbind = "unsetTokenProvider" ) - protected void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) { + protected void setTokenProvider(TokenProvider tokenProvider) { if (log.isDebugEnabled()) { - log.debug("Setting access token provider."); + log.debug("Setting token provider."); } - OAuth2ServiceComponentHolder.getInstance().setAccessTokenProvider(accessTokenProvider); + OAuth2ServiceComponentHolder.getInstance().setTokenProvider(tokenProvider); } /** - * Unsets the access token provider. + * Unsets the token provider. * - * @param accessTokenProvider AccessTokenProvider + * @param tokenProvider TokenProvider */ - protected void unsetAccessTokenProvider(AccessTokenProvider accessTokenProvider) { + protected void unsetTokenProvider(TokenProvider tokenProvider) { if (log.isDebugEnabled()) { - log.debug("Unset access token provider."); + log.debug("Unset token provider."); } - OAuth2ServiceComponentHolder.getInstance().setAccessTokenProvider(null); + OAuth2ServiceComponentHolder.getInstance().setTokenProvider(null); } /** 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 c2510df20bb..331bf333b5d 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 @@ -30,12 +30,12 @@ import org.wso2.carbon.identity.event.services.IdentityEventService; import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl; import org.wso2.carbon.identity.oauth.dto.ScopeDTO; -import org.wso2.carbon.identity.oauth.tokenprocessor.AccessTokenProvider; -import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultAccessTokenProvider; import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultOAuth2RevocationProcessor; import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultRefreshTokenGrantProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.DefaultTokenProvider; import org.wso2.carbon.identity.oauth.tokenprocessor.OAuth2RevocationProcessor; import org.wso2.carbon.identity.oauth.tokenprocessor.RefreshTokenGrantProcessor; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenProvider; import org.wso2.carbon.identity.oauth2.OAuthAuthorizationRequestBuilder; import org.wso2.carbon.identity.oauth2.authz.validators.ResponseTypeRequestValidator; import org.wso2.carbon.identity.oauth2.bean.Scope; @@ -106,7 +106,7 @@ public class OAuth2ServiceComponentHolder { private boolean isOrganizationManagementEnabled = false; private RefreshTokenGrantProcessor refreshTokenGrantProcessor; private OAuth2RevocationProcessor revocationProcessor; - private AccessTokenProvider accessTokenProvider; + private TokenProvider tokenProvider; private AuthorizedAPIManagementService authorizedAPIManagementService; private APIResourceManager apiResourceManager; private RoleManagementService roleManagementServiceV2; @@ -723,26 +723,26 @@ public void removeAuthorizationRequestBuilder(OAuthAuthorizationRequestBuilder o } /** - * Get access token provider. + * Get token provider. * - * @return AccessTokenProvider + * @return TokenProvider */ - public AccessTokenProvider getAccessTokenProvider() { + public TokenProvider getTokenProvider() { - if (accessTokenProvider == null) { - accessTokenProvider = new DefaultAccessTokenProvider(); + if (tokenProvider == null) { + tokenProvider = new DefaultTokenProvider(); } - return accessTokenProvider; + return tokenProvider; } /** - * Set access token provider. + * Set token provider. * - * @param accessTokenProvider AccessTokenProvider + * @param tokenProvider TokenProvider */ - public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) { + public void setTokenProvider(TokenProvider tokenProvider) { - this.accessTokenProvider = accessTokenProvider; + this.tokenProvider = tokenProvider; } public AuthorizedAPIManagementService getAuthorizedAPIManagementService() { diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/RefreshTokenValidationDataDO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/RefreshTokenValidationDataDO.java index 24ee65d9369..e00ec772c67 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/RefreshTokenValidationDataDO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/model/RefreshTokenValidationDataDO.java @@ -49,6 +49,7 @@ public class RefreshTokenValidationDataDO { private long accessTokenValidityInMillis; private AccessTokenExtendedAttributes accessTokenExtendedAttributes; + private boolean isConsented; public String getAccessToken() { return accessToken; @@ -150,4 +151,14 @@ public void setAccessTokenExtendedAttributes( this.accessTokenExtendedAttributes = accessTokenExtendedAttributes; } + + public boolean isConsented() { + + return isConsented; + } + + public void setConsented(boolean consented) { + + isConsented = consented; + } } 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 e646bfd1837..e6d779e0049 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 @@ -35,6 +35,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.base.IdentityConstants; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; @@ -44,6 +45,7 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; 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.internal.OAuth2ServiceComponentHolder; import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding; @@ -94,7 +96,6 @@ public class JWTTokenIssuer extends OauthTokenIssuerImpl { private static final String TOKEN_BINDING_TYPE = "binding_type"; private static final String DEFAULT_TYP_HEADER_VALUE = "at+jwt"; private static final String CNF = "cnf"; - private static final Log log = LogFactory.getLog(JWTTokenIssuer.class); private static final String INBOUND_AUTH2_TYPE = "oauth2"; private Algorithm signatureAlgorithm = null; @@ -492,7 +493,8 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe jwtClaimsSetBuilder.jwtID(UUID.randomUUID().toString()); jwtClaimsSetBuilder.notBeforeTime(new Date(curTimeInMillis)); jwtClaimsSetBuilder.claim(CLIENT_ID, consumerKey); - + setEntityIdClaim(jwtClaimsSetBuilder, authAuthzReqMessageContext, tokenReqMessageContext, authenticatedUser, + oAuthAppDO); String scope = getScope(authAuthzReqMessageContext, tokenReqMessageContext); if (StringUtils.isNotEmpty(scope)) { jwtClaimsSetBuilder.claim(SCOPE, scope); @@ -858,4 +860,43 @@ private boolean checkPairwiseSubEnabledForAccessTokens() { return Boolean.parseBoolean(IdentityUtil.getProperty(ENABLE_PPID_FOR_ACCESS_TOKENS)); } + + /** + * Set entity_id claim to the JWT if token persistence is disabled. This is to identify the principal subject of the + * issuing token. + * + * @param jwtClaimsSetBuilder JWT Claim Set Builder + * @param authAuthzReqMessageContext OAuthAuthzReqMessageContext + * @param tokenReqMessageContext OAuthTokenReqMessageContext + * @param authenticatedUser Authenticated User + * @param oAuthAppDO OAuthAppDO + * @throws IdentityOAuth2Exception If an error occurs while setting entity_id claim. + */ + private void setEntityIdClaim(JWTClaimsSet.Builder jwtClaimsSetBuilder, + OAuthAuthzReqMessageContext authAuthzReqMessageContext, + OAuthTokenReqMessageContext tokenReqMessageContext, + AuthenticatedUser authenticatedUser, + OAuthAppDO oAuthAppDO) throws IdentityOAuth2Exception { + + if (!OAuth2Util.isTokenPersistenceEnabled()) { + try { + /* + * The entity_id is used to identify the principal subject for the issuing token. For user access + * tokens, this is the user's unique ID. For application access tokens, this is the application's + * consumer key. + */ + String userType = getAuthorizedUserType(authAuthzReqMessageContext, tokenReqMessageContext); + if (OAuthConstants.UserType.APPLICATION_USER.equals(userType)) { + jwtClaimsSetBuilder.claim(OAuth2Constants.ENTITY_ID, authenticatedUser.getUserId()); + } else if (OAuthConstants.UserType.APPLICATION.equals(userType)) { + jwtClaimsSetBuilder.claim(OAuth2Constants.ENTITY_ID, oAuthAppDO.getOauthConsumerKey()); + } else { + throw new IdentityOAuth2Exception("Invalid user type: " + userType); + } + } catch (UserIdNotFoundException e) { + throw new IdentityOAuth2Exception("User id not found for user: " + + authenticatedUser.getLoggableMaskedUserId(), 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 c86cde0edd7..034dd9d0dd2 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 @@ -540,49 +540,55 @@ private void updateCacheIfEnabled(AccessTokenDO newTokenBean, String scope, Oaut throws IdentityOAuth2Exception { if (isHashDisabled && cacheEnabled) { - AccessTokenDO tokenToCache = AccessTokenDO.clone(newTokenBean); - // If usePersistedAccessTokenAlias is enabled then in the DB the - // access token alias taken from the OauthTokenIssuer's getAccessTokenHash - // method is set as the token. - if (oauthTokenIssuer.usePersistedAccessTokenAlias()) { - try { - String persistedTokenIdentifier = - oauthTokenIssuer.getAccessTokenHash(newTokenBean.getAccessToken()); - tokenToCache.setAccessToken(persistedTokenIdentifier); - } catch (OAuthSystemException e) { - if (log.isDebugEnabled()) { - if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { - log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + - " failed to parse the received token: " + tokenToCache.getAccessToken(), e); - } else { - log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + - " failed to parse the received token.", e); + /* + * If no token persistence, the token will be not be cached against a cache key with userId, scope, client, + * idp and binding reference. But, token will be cached and managed as an AccessTokenDO against the + * token identifier. + */ + if (OAuth2Util.isTokenPersistenceEnabled()) { + AccessTokenDO tokenToCache = AccessTokenDO.clone(newTokenBean); + // If usePersistedAccessTokenAlias is enabled then in the DB the + // access token alias taken from the OauthTokenIssuer's getAccessTokenHash + // method is set as the token. + if (oauthTokenIssuer.usePersistedAccessTokenAlias()) { + try { + String persistedTokenIdentifier = + oauthTokenIssuer.getAccessTokenHash(newTokenBean.getAccessToken()); + tokenToCache.setAccessToken(persistedTokenIdentifier); + } catch (OAuthSystemException e) { + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { + log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + + " failed to parse the received token: " + tokenToCache.getAccessToken(), e); + } else { + log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + + " failed to parse the received token.", e); + } } } } - } - String userId; - String authorizedOrganization; - try { - userId = tokenToCache.getAuthzUser().getUserId(); - authorizedOrganization = tokenToCache.getAuthzUser().getAccessingOrganization(); - if (StringUtils.isBlank(authorizedOrganization)) { - authorizedOrganization = OAuthConstants.AuthorizedOrganization.NONE; + String userId; + String authorizedOrganization; + try { + userId = tokenToCache.getAuthzUser().getUserId(); + authorizedOrganization = tokenToCache.getAuthzUser().getAccessingOrganization(); + if (StringUtils.isBlank(authorizedOrganization)) { + authorizedOrganization = OAuthConstants.AuthorizedOrganization.NONE; + } + } catch (UserIdNotFoundException e) { + throw new IdentityOAuth2Exception( + "User id is not available for user: " + tokenToCache.getAuthzUser().getLoggableUserId(), e); } - } catch (UserIdNotFoundException e) { - throw new IdentityOAuth2Exception( - "User id is not available for user: " + tokenToCache.getAuthzUser().getLoggableUserId(), e); - } - String authenticatedIDP = OAuth2Util.getAuthenticatedIDP(tokenToCache.getAuthzUser()); - OAuthCacheKey cacheKey = getOAuthCacheKey(scope, tokenToCache.getConsumerKey(), userId, authenticatedIDP, - getTokenBindingReference(tokenToCache), authorizedOrganization); - oauthCache.addToCache(cacheKey, tokenToCache); - if (log.isDebugEnabled()) { - log.debug("Access token was added to OAuthCache with cache key : " + cacheKey.getCacheKeyString()); + String authenticatedIDP = OAuth2Util.getAuthenticatedIDP(tokenToCache.getAuthzUser()); + OAuthCacheKey cacheKey = getOAuthCacheKey(scope, tokenToCache.getConsumerKey(), userId, + authenticatedIDP, getTokenBindingReference(tokenToCache), authorizedOrganization); + oauthCache.addToCache(cacheKey, tokenToCache); + if (log.isDebugEnabled()) { + log.debug("Access token was added to OAuthCache with cache key : " + cacheKey.getCacheKeyString()); + } } - // Adding AccessTokenDO to improve validation performance OAuth2Util.addTokenDOtoCache(newTokenBean); } @@ -880,7 +886,11 @@ private AccessTokenDO getExistingToken(OAuthTokenReqMessageContext tokenMsgCtx, String tokenBindingReference = getTokenBindingReference(tokenMsgCtx); String authorizedOrganization = getAuthorizedOrganization(tokenMsgCtx); - if (cacheEnabled) { + if (cacheEnabled && OAuth2Util.isTokenPersistenceEnabled()) { + /* + * If no token persistence, the token is not cached against a cache key with userId, scope, client, + * idp and binding reference as, multiple active tokens can exist for the same key. + */ existingToken = getExistingTokenFromCache(cacheKey, tokenReq.getClientId(), tokenMsgCtx.getAuthorizedUser().getLoggableUserId(), scope, tokenBindingReference, authorizedOrganization, tokenMsgCtx.getAuthorizedUser().getTenantDomain()); 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 2ca7c7e4a29..e6a7b224904 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 @@ -306,37 +306,43 @@ private void updateCacheIfEnabled(OAuthTokenReqMessageContext tokReqMsgCtx, Acce OAuthCache.getInstance().clearCacheEntry(accessTokenCacheKey, oldAccessToken.getAuthorizedUser().getTenantDomain()); } - AccessTokenDO tokenToCache = AccessTokenDO.clone(accessTokenBean); - OauthTokenIssuer oauthTokenIssuer; - try { - oauthTokenIssuer = OAuth2Util.getOAuthTokenIssuerForOAuthApp( - tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId()); - } catch (InvalidOAuthClientException e) { - throw new IdentityOAuth2Exception( - "Error while retrieving oauth issuer for the app with clientId: " + - tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId(), e); - } - if (oauthTokenIssuer.usePersistedAccessTokenAlias()) { + /* + * If no token persistence, the token will be not be cached against a cache key with userId, scope, client, + * idp and binding reference. But, token will be cached and managed as an AccessTokenDO against the + * token identifier. + */ + if (OAuth2Util.isTokenPersistenceEnabled()) { + AccessTokenDO tokenToCache = AccessTokenDO.clone(accessTokenBean); + OauthTokenIssuer oauthTokenIssuer; try { - String persistedTokenIdentifier = - oauthTokenIssuer.getAccessTokenHash(accessTokenBean.getAccessToken()); - tokenToCache.setAccessToken(persistedTokenIdentifier); - } catch (OAuthSystemException e) { - if (log.isDebugEnabled()) { - if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { - log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + - " failed to parse the received token " + tokenToCache.getAccessToken(), e); - } else { - log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + - " failed to parse the received token.", e); + oauthTokenIssuer = OAuth2Util.getOAuthTokenIssuerForOAuthApp( + tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId()); + } catch (InvalidOAuthClientException e) { + throw new IdentityOAuth2Exception( + "Error while retrieving oauth issuer for the app with clientId: " + + tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId(), e); + } + if (oauthTokenIssuer.usePersistedAccessTokenAlias()) { + try { + String persistedTokenIdentifier = + oauthTokenIssuer.getAccessTokenHash(accessTokenBean.getAccessToken()); + tokenToCache.setAccessToken(persistedTokenIdentifier); + } catch (OAuthSystemException e) { + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { + log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + + " failed to parse the received token " + tokenToCache.getAccessToken(), e); + } else { + log.debug("Token issuer: " + oauthTokenIssuer.getClass() + " was tried and" + + " failed to parse the received token.", e); + } } } } - } - - // Add new access token to the OAuthCache - OAuthCache.getInstance().addToCache(oauthCacheKey, tokenToCache); + // Add new access token to the OAuthCache + OAuthCache.getInstance().addToCache(oauthCacheKey, tokenToCache); + } // Add new access token to the AccessTokenCache OAuth2Util.addTokenDOtoCache(accessTokenBean); 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 1637d7d6217..70cc0550aaf 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 @@ -114,6 +114,7 @@ import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.IdentityOAuth2ScopeException; import org.wso2.carbon.identity.oauth2.IdentityOAuth2ScopeServerException; +import org.wso2.carbon.identity.oauth2.OAuth2Constants; import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext; import org.wso2.carbon.identity.oauth2.bean.Scope; @@ -4224,7 +4225,7 @@ public static String getAccessTokenIdentifier(OAuth2TokenValidationResponseDTO t if (tokenResponse.getAuthorizationContextToken().getTokenString() != null) { AccessTokenDO accessTokenDO; try { - accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(tokenResponse.getAuthorizationContextToken().getTokenString(), false); } catch (IdentityOAuth2Exception e) { throw new UserInfoEndpointException("Error occurred while obtaining access token.", e); @@ -4251,7 +4252,7 @@ public static Optional getAccessTokenDO(OAuth2TokenValidationResp if (tokenResponse.getAuthorizationContextToken().getTokenString() != null) { try { - AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(tokenResponse.getAuthorizationContextToken().getTokenString(), false); return Optional.ofNullable(accessTokenDO); } catch (IdentityOAuth2Exception e) { @@ -5029,4 +5030,17 @@ public static String[] getSupportedClientAuthMethods() { } return supportedClientAuthMethods.toArray(new String[0]); } + + /** + * Check if token persistence is enabled. + * + * @return True if token persistence is enabled. + */ + public static boolean isTokenPersistenceEnabled() { + + if (IdentityUtil.getProperty(OAuth2Constants.OAUTH_TOKEN_PERSISTENCE_ENABLE) != null) { + return Boolean.parseBoolean(IdentityUtil.getProperty(OAuth2Constants.OAUTH_TOKEN_PERSISTENCE_ENABLE)); + } + return OAuth2Constants.DEFAULT_PERSIST_ENABLED; + } } 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 59f5f73256c..e2d6ec775a6 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 @@ -32,7 +32,7 @@ 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; -import org.wso2.carbon.identity.oauth.tokenprocessor.AccessTokenProvider; +import org.wso2.carbon.identity.oauth.tokenprocessor.TokenProvider; import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.authcontext.AuthorizationContextTokenGenerator; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; @@ -62,7 +62,7 @@ public class TokenValidationHandler { AuthorizationContextTokenGenerator tokenGenerator = null; private static final Log log = LogFactory.getLog(TokenValidationHandler.class); private Map tokenValidators = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - private AccessTokenProvider tokenValidationProcessor; + private TokenProvider tokenValidationProcessor; private static final String BEARER_TOKEN_TYPE = "Bearer"; private static final String BEARER_TOKEN_TYPE_JWT = "jwt"; private static final String BUILD_FQU_FROM_SP_CONFIG = "OAuth.BuildSubjectIdentifierFromSPConfig"; @@ -120,7 +120,7 @@ private TokenValidationHandler() { log.error(errorMsg, e); } } - tokenValidationProcessor = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider(); + tokenValidationProcessor = OAuth2ServiceComponentHolder.getInstance().getTokenProvider(); } public static TokenValidationHandler getInstance() { @@ -178,7 +178,7 @@ public OAuth2ClientApplicationDTO findOAuthConsumerIfTokenIsValid(OAuth2TokenVal } try { - accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(requestDTO.getAccessToken().getIdentifier(), false); } catch (IllegalArgumentException e) { // Access token not found in the system. @@ -276,7 +276,7 @@ public OAuth2IntrospectionResponseDTO buildIntrospectionResponse(OAuth2TokenVali // Adding the AccessTokenDO as a context property for further use AccessTokenDO accessTokenDO; try { - accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(oAuth2Token.getIdentifier(), true); if (accessTokenDO != null) { messageContext.addProperty(OAuthConstants.ACCESS_TOKEN_DO, accessTokenDO); @@ -500,7 +500,7 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation } else { try { String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(validationRequest.getAccessToken().getIdentifier(), false); boolean isCrossTenantTokenIntrospectionAllowed = OAuthServerConfiguration.getInstance().isCrossTenantTokenIntrospectionAllowed(); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java index 2482899c8df..b747b0f6207 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java @@ -106,7 +106,7 @@ private Map filterOIDCClaims(OAuth2TokenValidationResponseDTO to AccessTokenDO accessTokenDO; String accessToken; try { - accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(tokenResponse.getAuthorizationContextToken().getTokenString(), false); accessToken = accessTokenDO == null ? null : accessTokenDO.getAccessToken(); } catch (IdentityOAuth2Exception e) { @@ -181,7 +181,7 @@ protected String getSubjectClaim(Map userClaims, AuthenticatedUser authenticatedUser; try { authenticatedUser = OAuth2Util.getAuthenticatedUser(OAuth2ServiceComponentHolder.getInstance() - .getAccessTokenProvider().getVerifiedAccessToken( + .getTokenProvider().getVerifiedAccessToken( tokenResponse.getAuthorizationContextToken().getTokenString(), false)); } catch (IdentityOAuth2Exception e) { throw new UserInfoEndpointException("Error occurred while obtaining access token.", e); @@ -227,7 +227,7 @@ protected Map getUserClaimsFilteredByConsent(OAuth2TokenValidati String grantType; try { String accessToken = validationResponseDTO.getAuthorizationContextToken().getTokenString(); - AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getAccessTokenProvider() + AccessTokenDO accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider() .getVerifiedAccessToken(accessToken, false); grantType = getGrantType(accessTokenDO); if (OAuth2ServiceComponentHolder.isConsentedTokenColumnEnabled()) { 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 5aa04fa8650..9d632e94bfa 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 @@ -42,6 +42,7 @@ import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; 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.dto.OAuth2AccessTokenReqDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO; @@ -67,6 +68,7 @@ import java.util.Map; import java.util.UUID; +import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyObject; @@ -116,6 +118,7 @@ public class JWTTokenIssuerTest extends PowerMockIdentityBaseTest { private static final String DUMMY_CLIENT_ID = "dummyClientID"; private static final String DUMMY_SECTOR_IDENTIFIER = "https://mockhost.com/file_of_redirect_uris.json"; private static final String DUMMY_CONSUMER_KEY = "DUMMY_CONSUMER_KEY"; + private static final String DUMMY_USER_ID = "DUMMY_USER_ID"; private static final String ID_TOKEN_ISSUER = "idTokenIssuer"; private static final String EXPIRY_TIME_JWT = "EXPIRY_TIME_JWT"; @@ -168,11 +171,12 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[], accessTokenReqDTO.setClientId(DUMMY_CLIENT_ID); OAuthTokenReqMessageContext reqMessageContext = new OAuthTokenReqMessageContext(accessTokenReqDTO); reqMessageContext.setScope(requestScopes); - + reqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER); AuthenticatedUser authenticatedUser = new AuthenticatedUser(); authenticatedUser.setUserName("DUMMY_USERNAME"); authenticatedUser.setTenantDomain("DUMMY_TENANT.COM"); authenticatedUser.setUserStoreDomain("DUMMY_DOMAIN"); + authenticatedUser.setUserId(DUMMY_USER_ID); reqMessageContext.setAuthorizedUser(authenticatedUser); OAuth2ServiceComponentHolder.getInstance().addJWTAccessTokenClaimProvider( @@ -204,11 +208,12 @@ public void testBuildJWTTokenFromAuthzMsgContext(String requestScopes[], OAuth2AuthorizeReqDTO authorizeReqDTO = new OAuth2AuthorizeReqDTO(); OAuthAuthzReqMessageContext authzReqMessageContext = new OAuthAuthzReqMessageContext(authorizeReqDTO); authzReqMessageContext.setApprovedScope(requestScopes); - + authzReqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER); AuthenticatedUser authenticatedUser = new AuthenticatedUser(); authenticatedUser.setUserName("DUMMY_USERNAME"); authenticatedUser.setTenantDomain("DUMMY_TENANT.COM"); authenticatedUser.setUserStoreDomain("DUMMY_DOMAIN"); + authenticatedUser.setUserId(DUMMY_USER_ID); authorizeReqDTO.setUser(authenticatedUser); authorizeReqDTO.setConsumerKey(DUMMY_CONSUMER_KEY); @@ -252,8 +257,8 @@ public void testCreateJWTClaimSetForInvalidClient() throws Exception { mockStatic(OAuth2Util.class); when(OAuth2Util.getAppInformationByClientId(null)) .thenThrow(new InvalidOAuthClientException("INVALID_CLIENT")); + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(true); when(oAuthServerConfiguration.getSignatureAlgorithm()).thenReturn(SHA256_WITH_HMAC); - JWTTokenIssuer jwtTokenIssuer = new JWTTokenIssuer(); jwtTokenIssuer.createJWTClaimSet(null, null, null); } @@ -264,6 +269,7 @@ public Object[][] provideClaimSetData() { authenticatedUser.setUserName("DUMMY_USERNAME"); authenticatedUser.setTenantDomain("DUMMY_TENANT.COM"); authenticatedUser.setUserStoreDomain("DUMMY_DOMAIN"); + authenticatedUser.setUserId(DUMMY_USER_ID); final String authenticatedSubjectIdentifier = authenticatedUser.toString(); authenticatedUser.setAuthenticatedSubjectIdentifier(authenticatedSubjectIdentifier); @@ -272,7 +278,7 @@ public Object[][] provideClaimSetData() { authorizeReqDTO.setTenantDomain("super.wso2"); authorizeReqDTO.setUser(authenticatedUser); OAuthAuthzReqMessageContext authzReqMessageContext = new OAuthAuthzReqMessageContext(authorizeReqDTO); - authzReqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION); + authzReqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER); OAuth2AccessTokenReqDTO tokenReqDTO = new OAuth2AccessTokenReqDTO(); tokenReqDTO.setGrantType(APPLICATION_ACCESS_TOKEN_GRANT_TYPE); @@ -283,7 +289,7 @@ public Object[][] provideClaimSetData() { cal.setTime(new Date()); // sets calendar time/date cal.add(Calendar.HOUR_OF_DAY, 1); // adds one hour tokenReqMessageContext.addProperty(EXPIRY_TIME_JWT, cal.getTime()); - tokenReqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER); + tokenReqMessageContext.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION); return new Object[][]{ { @@ -326,6 +332,7 @@ public void testCreateJWTClaimSet(Object authzReqMessageContext, OAuthAppDO appDO = spy(new OAuthAppDO()); appDO.setSubjectType("pairwise"); appDO.setSectorIdentifierURI(DUMMY_SECTOR_IDENTIFIER); + appDO.setOauthConsumerKey(DUMMY_CLIENT_ID); mockGrantHandlers(); mockCustomClaimsCallbackHandler(); mockStatic(OAuth2Util.class); @@ -334,6 +341,7 @@ public void testCreateJWTClaimSet(Object authzReqMessageContext, when(OAuth2Util.getIdTokenIssuer(anyString())).thenReturn(ID_TOKEN_ISSUER); when(OAuth2Util.getOIDCAudience(anyString(), anyObject())).thenReturn(Collections.singletonList (DUMMY_CLIENT_ID)); + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(true); when(oAuthServerConfiguration.getSignatureAlgorithm()).thenReturn(SHA256_WITH_HMAC); when(oAuthServerConfiguration.getUserAccessTokenValidityPeriodInSeconds()) @@ -380,7 +388,23 @@ public void testCreateJWTClaimSet(Object authzReqMessageContext, assertEquals(new Duration(jwtClaimSet.getIssueTime().getTime(), jwtClaimSet.getExpirationTime().getTime()) .getMillis(), expectedExpiry); } - + assertNull(jwtClaimSet.getClaim(OAuth2Constants.ENTITY_ID)); + // The entity_id claim is a mandatory claim in the JWT when token persistence is disabled. + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(false); + jwtClaimSet = jwtTokenIssuer.createJWTClaimSet( + (OAuthAuthzReqMessageContext) authzReqMessageContext, + (OAuthTokenReqMessageContext) tokenReqMessageContext, + DUMMY_CLIENT_ID + ); + assertNotNull(jwtClaimSet.getClaim(OAuth2Constants.ENTITY_ID)); + if (tokenReqMessageContext != null) { + assertEquals(jwtClaimSet.getClaim(OAuth2Constants.ENTITY_ID), DUMMY_CLIENT_ID); + } + if (authzReqMessageContext != null) { + assertEquals(jwtClaimSet.getClaim(OAuth2Constants.ENTITY_ID), DUMMY_USER_ID); + } + // Enabling persistence back for the rest of the test cases. + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(true); } @Test(dataProvider = "createJWTClaimSetDataProvider") @@ -395,6 +419,7 @@ public void testSignJWTWithRSA(Object authzReqMessageContext, mockStatic(OAuth2Util.class); when(OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(appDO); when(OAuth2Util.getThumbPrint(anyString(), anyInt())).thenReturn(THUMBPRINT); + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(true); System.setProperty(CarbonBaseConstants.CARBON_HOME, Paths.get(System.getProperty("user.dir"), "src", "test", "resources").toString()); @@ -680,6 +705,7 @@ private void prepareForBuildJWTToken() throws IdentityOAuth2Exception, InvalidOA mockStatic(OAuth2Util.class); when(OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(appDO); when(OAuth2Util.getTenantDomain(anyInt())).thenReturn("super.wso2"); + when(OAuth2Util.isTokenPersistenceEnabled()).thenReturn(true); } static class DummyTestJWTAccessTokenClaimProvider implements JWTAccessTokenClaimProvider {