Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix JWT token issuing performance issue when renew_token_without_revoking_existing is enabled #2673

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@
import java.util.Set;
import java.util.UUID;

import static org.wso2.carbon.identity.oauth.common.OAuthConstants.RENEW_TOKEN_WITHOUT_REVOKING_EXISTING_ENABLE_CONFIG;
import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenBindings.NONE;
import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE;
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.JWT;

/**
* Abstract authorization grant handler.
Expand All @@ -86,6 +88,9 @@ public abstract class AbstractAuthorizationGrantHandler implements Authorization
protected static final String EXISTING_TOKEN_ISSUED = "existingTokenUsed";
protected static final int SECONDS_TO_MILISECONDS_FACTOR = 1000;
private boolean isHashDisabled = OAuth2Util.isHashDisabled();
private static final boolean renewWithoutRevokingExistingEnabled = Boolean.parseBoolean(IdentityUtil.
getProperty(RENEW_TOKEN_WITHOUT_REVOKING_EXISTING_ENABLE_CONFIG));
public static final String OAUTH_APP = "OAuthAppDO";

@Override
public void init() throws IdentityOAuth2Exception {
Expand Down Expand Up @@ -148,6 +153,29 @@ public OAuth2AccessTokenRespDTO issue(OAuthTokenReqMessageContext tokReqMsgCtx)

synchronized ((consumerKey + ":" + authorizedUserId + ":" + scope + ":" + tokenBindingReference).intern()) {
AccessTokenDO existingTokenBean = null;

OAuthAppDO oAuthAppDO = (OAuthAppDO) tokReqMsgCtx.getProperty(OAUTH_APP);
String tokenType = (oAuthAppDO != null) ? oAuthAppDO.getTokenType() : null;

/*
Check if the token type is JWT and renew without revoking existing tokens is enabled.
Additionally, ensure that the grant type used for the token request is allowed to renew without revoke,
based on the config.
*/
if (JWT.equals(tokenType) && renewWithoutRevokingExistingEnabled &&
OAuth2ServiceComponentHolder.getJwtRenewWithoutRevokeAllowedGrantTypes()
.contains(tokReqMsgCtx.getOauth2AccessTokenReqDTO().getGrantType())) {
/*
If the application does not have a token binding type (i.e., no specific binding type is set),
binding reference will be randomly generated UUID, in that case we can generate a new access token
without looking up the existing tokens in the token table.
*/
if (oAuthAppDO.getTokenBindingType() == null) {
return generateNewAccessToken(tokReqMsgCtx, scope, consumerKey, existingTokenBean,
false, oauthTokenIssuer);
}
}

if (isHashDisabled) {
existingTokenBean = getExistingToken(tokReqMsgCtx,
getOAuthCacheKey(scope, consumerKey, authorizedUserId, authenticatedIDP,
Expand Down Expand Up @@ -1056,18 +1084,65 @@ private boolean hasValidationByApplicationScopeValidatorsFailed(OAuthTokenReqMes
* @param tokReqMsgCtx OAuthTokenReqMessageContext.
* @return token binding reference.
*/

private String getTokenBindingReference(OAuthTokenReqMessageContext tokReqMsgCtx) {

if (tokReqMsgCtx.getTokenBinding() == null) {
if (log.isDebugEnabled()) {
log.debug("Token binding data is null.");
/**
* If OAuth.JWT.RenewTokenWithoutRevokingExisting is enabled from configurations, and current token
* binding is null,then we will add a new token binding (request binding) to the token binding with
* a value of a random UUID.
* The purpose of this new token binding type is to add a random value to the token binding so that
* "User, Application, Scope, Binding" combination will be unique for each token.
* Previously, if a token issue request come for the same combination of "User, Application, Scope, Binding",
* the existing JWT token will be revoked and issue a new token. but with this way, we can issue new tokens
* without revoking the old ones.
*
* Add following configuration to deployment.toml file to enable this feature.
* [oauth.jwt.renew_token_without_revoking_existing]
* enable = true
*
* By default, the allowed grant type for this feature is "client_credentials". If you need to enable for
* other grant types, add the following configuration to deployment.toml file.
* [oauth.jwt.renew_token_without_revoking_existing]
* enable = true
* allowed_grant_types = ["client_credentials","password", ...]
*/

OAuthAppDO oAuthAppDO = (OAuthAppDO) tokReqMsgCtx.getProperty(OAUTH_APP);
String tokenType = (oAuthAppDO != null) ? oAuthAppDO.getTokenType() : null;

/*
Check if the token type is JWT and renew without revoking existing tokens is enabled.
Additionally, ensure that the grant type used for the token request is allowed to renew without revoke,
based on the config.
*/
if (JWT.equalsIgnoreCase(tokenType)) {

if (renewWithoutRevokingExistingEnabled && tokReqMsgCtx != null && (tokReqMsgCtx.getTokenBinding() == null
|| StringUtils.isBlank(tokReqMsgCtx.getTokenBinding().getBindingReference()))) {
if (OAuth2ServiceComponentHolder.getJwtRenewWithoutRevokeAllowedGrantTypes()
.contains(tokReqMsgCtx.getOauth2AccessTokenReqDTO().getGrantType())) {
return UUID.randomUUID().toString();
}
return NONE;
}
return NONE;
}
if (StringUtils.isBlank(tokReqMsgCtx.getTokenBinding().getBindingReference())) {
return getExistingTokenBindingReference(tokReqMsgCtx);
}

/**
* Retrieves the existing token binding reference if available, otherwise returns NONE.
*
* @param tokReqMsgCtx OAuthTokenReqMessageContext.
* @return token binding reference.
*/
private String getExistingTokenBindingReference(OAuthTokenReqMessageContext tokReqMsgCtx) {

if (tokReqMsgCtx == null || tokReqMsgCtx.getTokenBinding() == null) {
return NONE;
}
return tokReqMsgCtx.getTokenBinding().getBindingReference();
String bindingReference = tokReqMsgCtx.getTokenBinding().getBindingReference();
return StringUtils.isBlank(bindingReference) ? NONE : bindingReference;
}

/**
Expand Down