Skip to content

Commit

Permalink
refactor code.
Browse files Browse the repository at this point in the history
handle refresh token expiration time.
  • Loading branch information
dushaniw committed Nov 2, 2023
1 parent e76a28d commit ad5239d
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ public static class JWTClaim {
public static final String AUDIENCE = "aud";
public static final String CLIENT_ID = "client_id";
public static final String SCOPE = "scope";
public static final String GIVEN_NAME = "given_name";
public static final String AUTHORIZATION_PARTY = "azp";
public static final String IS_CONSENTED = "is_consented";
public static final String TOKEN_TYPE_ELEM = "token_type";
}
public static final int SECONDS_TO_MILISECONDS_FACTOR = 1000;
public static final String PREV_ACCESS_TOKEN = "previousAccessToken";

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,15 @@ public boolean isInvalidToken(String token, String consumerKey) throws IdentityO
public void addInvalidToken(String token, String consumerKey, Long expiryTime)
throws IdentityOAuth2Exception {

log.debug("Insert invalid toke to the database");
if (log.isDebugEnabled()) {
if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) {
log.debug(String.format("Insert invalid token (hashed): %s for consumer key: %s with expiry time: %s",
DigestUtils.sha256Hex(token), consumerKey, expiryTime));
} else {
log.debug(String.format("Insert invalid token for consumer key: %s with expiry time: %s",
DigestUtils.sha256Hex(token), consumerKey, expiryTime));
}
}
try (Connection connection = PersistenceDatabaseUtil.getConnection()) {
try (PreparedStatement preparedStatement = connection.prepareStatement(SQLQueries.INSERT_INVALID_TOKEN)) {
connection.setAutoCommit(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext;
import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;
import org.wso2.carbon.identity.oauth2.token.JWTTokenIssuer;
import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
Expand Down Expand Up @@ -154,6 +155,7 @@ protected JWTClaimsSet createJWTClaimSetForRefreshTokens(OAuthAuthzReqMessageCon
// loading the stored application data.
OAuthAppDO oAuthAppDO;
String spTenantDomain;
long refreshTokenLifeTimeInMillis;
try {
if (authAuthzReqMessageContext != null) {
spTenantDomain = authAuthzReqMessageContext.getAuthorizationReqDTO().getTenantDomain();
Expand All @@ -164,17 +166,24 @@ protected JWTClaimsSet createJWTClaimSetForRefreshTokens(OAuthAuthzReqMessageCon
} catch (InvalidOAuthClientException e) {
throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + consumerKey, e);
}
long refreshTokenLifeTimeInMillis = getRefreshTokenLifeTimeInMillis(oAuthAppDO);
String issuer = OAuth2Util.getIdTokenIssuer(spTenantDomain);
long curTimeInMillis = Calendar.getInstance().getTimeInMillis();
AuthenticatedUser authenticatedUser = getAuthenticatedUser(authAuthzReqMessageContext, tokenReqMessageContext);
String sub = getSubjectClaim(authenticatedUser);
// Set the default claims.
JWTClaimsSet.Builder jwtClaimsSetBuilder = new JWTClaimsSet.Builder();
jwtClaimsSetBuilder.issuer(issuer);
jwtClaimsSetBuilder.subject(sub);
jwtClaimsSetBuilder.claim(PersistenceConstants.JWTClaim.AUTHORIZATION_PARTY, consumerKey);
jwtClaimsSetBuilder.issueTime(new Date(curTimeInMillis));
if (tokenReqMessageContext != null) {
refreshTokenLifeTimeInMillis = getRefreshTokenLifeTimeInMillis(oAuthAppDO, tokenReqMessageContext);
} else {
refreshTokenLifeTimeInMillis = getRefreshTokenLifeTimeInMillis(oAuthAppDO, authAuthzReqMessageContext);
}
long curTimeInMillis = Calendar.getInstance().getTimeInMillis();
Date issuedTime = new Date(curTimeInMillis);
jwtClaimsSetBuilder.issueTime(getRefreshTokenIssuedTime(tokenReqMessageContext, oAuthAppDO, issuedTime));
jwtClaimsSetBuilder.expirationTime(
calculateRefreshTokenExpiryTime(refreshTokenLifeTimeInMillis, curTimeInMillis));
jwtClaimsSetBuilder.jwtID(UUID.randomUUID().toString());
jwtClaimsSetBuilder.claim(PersistenceConstants.JWTClaim.CLIENT_ID, consumerKey);
setEntityIdClaim(jwtClaimsSetBuilder, authAuthzReqMessageContext, tokenReqMessageContext, authenticatedUser,
Expand All @@ -185,8 +194,6 @@ protected JWTClaimsSet createJWTClaimSetForRefreshTokens(OAuthAuthzReqMessageCon
}
// claim to identify the JWT as a refresh token.
jwtClaimsSetBuilder.claim(PersistenceConstants.JWTClaim.TOKEN_TYPE_ELEM, PersistenceConstants.REFRESH_TOKEN);
jwtClaimsSetBuilder.expirationTime(
calculateRefreshTokenExpiryTime(refreshTokenLifeTimeInMillis, curTimeInMillis));
/*
* This is a spec (openid-connect-core-1_0:2.0) requirement for ID tokens. But we are keeping this in JWT as
* well.
Expand All @@ -208,31 +215,136 @@ protected JWTClaimsSet createJWTClaimSetForRefreshTokens(OAuthAuthzReqMessageCon
}

/**
* Get token validity period for the Self contained JWT Access Token.
* Get token validity period for the Self contained JWT Access Token from OAuthApp or OAuthServer Configuration.
*
* @param oAuthAppDO OAuthApp
* @return Refresh Token Life Time in milliseconds
*/
protected long getRefreshTokenLifeTimeInMillis(OAuthAppDO oAuthAppDO) {
private long getRefreshTokenLifeTimeInMillisFromConfig(OAuthAppDO oAuthAppDO) {

long lifetimeInMillis;
String consumerKey = oAuthAppDO.getOauthConsumerKey();
long refreshTokenValidityPeriodInMillis;
if (oAuthAppDO.getRefreshTokenExpiryTime() != 0) {
lifetimeInMillis = oAuthAppDO.getRefreshTokenExpiryTime() * 1000;
refreshTokenValidityPeriodInMillis =
oAuthAppDO.getRefreshTokenExpiryTime() * PersistenceConstants.SECONDS_TO_MILISECONDS_FACTOR;
if (log.isDebugEnabled()) {
log.debug("Refresh Token Life time set to : " + lifetimeInMillis + "ms.");
log.debug("OAuth application id : " + consumerKey + ", refresh token validity time " +
refreshTokenValidityPeriodInMillis + "ms");
}
} else {
lifetimeInMillis = OAuthServerConfiguration.getInstance()
.getRefreshTokenValidityPeriodInSeconds() * 1000;
refreshTokenValidityPeriodInMillis = OAuthServerConfiguration.getInstance()
.getRefreshTokenValidityPeriodInSeconds() * PersistenceConstants.SECONDS_TO_MILISECONDS_FACTOR;
}
return refreshTokenValidityPeriodInMillis;
}

/**
* Get token validity period for the Self contained JWT Access Token.
*
* @param oAuthAppDO OAuthApp
* @param tokenReqMessageContext TokenRequestMessageContext
* @return Refresh Token Life Time in milliseconds
*/
private long getRefreshTokenLifeTimeInMillis(OAuthAppDO oAuthAppDO,
OAuthTokenReqMessageContext tokenReqMessageContext) {

String consumerKey = oAuthAppDO.getOauthConsumerKey();
long refreshTokenValidityPeriodInMillis = 0;
long validityPeriodFromMsgContext = tokenReqMessageContext.getRefreshTokenvalidityPeriod();
if (validityPeriodFromMsgContext > 0) {
refreshTokenValidityPeriodInMillis = validityPeriodFromMsgContext *
PersistenceConstants.SECONDS_TO_MILISECONDS_FACTOR;
if (log.isDebugEnabled()) {
log.debug("OAuth application id : " + consumerKey + ", using refresh token " +
"validity period configured from OAuthTokenReqMessageContext: " +
refreshTokenValidityPeriodInMillis + " ms");
}
} else if (tokenReqMessageContext.getProperty(PersistenceConstants.PREV_ACCESS_TOKEN) != null) {
RefreshTokenValidationDataDO validationBean =
(RefreshTokenValidationDataDO) tokenReqMessageContext.getProperty(
PersistenceConstants.PREV_ACCESS_TOKEN);
if (isRenewRefreshToken(oAuthAppDO.getRenewRefreshTokenEnabled())
&& !OAuthServerConfiguration.getInstance().isExtendRenewedTokenExpiryTimeEnabled()) {
// If refresh token renewal enabled and extend token expiry disabled, set the old token issued and
// validity.
refreshTokenValidityPeriodInMillis = validationBean.getValidityPeriodInMillis();
}
}
if (refreshTokenValidityPeriodInMillis == 0) {
refreshTokenValidityPeriodInMillis = getRefreshTokenLifeTimeInMillisFromConfig(oAuthAppDO);
}
if (log.isDebugEnabled()) {
log.debug("JWT Self Signed Refresh Token Life time set to : " + refreshTokenValidityPeriodInMillis + "ms.");
}
return refreshTokenValidityPeriodInMillis;
}

/**
* Get token validity period for the Self contained JWT Access Token.
*
* @param oAuthAppBean OAuthApp
* @param oauthAuthzMsgCtx OAuthAuthhorizationRequestMessageContext
* @return Refresh Token Life Time in milliseconds
*/
private long getRefreshTokenLifeTimeInMillis(OAuthAppDO oAuthAppBean,
OAuthAuthzReqMessageContext oauthAuthzMsgCtx) {

long refreshTokenValidityPeriodInMillis = 0;
long refreshTokenValidityPeriod = oauthAuthzMsgCtx.getRefreshTokenvalidityPeriod();
if (refreshTokenValidityPeriod > 0) {
refreshTokenValidityPeriodInMillis = oauthAuthzMsgCtx.getRefreshTokenvalidityPeriod() *
PersistenceConstants.SECONDS_TO_MILISECONDS_FACTOR;
if (log.isDebugEnabled()) {
log.debug("Application specific refresh token expiry time was 0ms. Setting default refresh token "
+ "lifetime : " + lifetimeInMillis + "ms.");
log.debug("OAuth application id : " + oAuthAppBean.getOauthConsumerKey() + ", using refresh token " +
"validity period configured from OAuthAuthzReqMessageContext: " +
refreshTokenValidityPeriodInMillis + " ms");
}
}
if (refreshTokenValidityPeriodInMillis == 0) {
refreshTokenValidityPeriodInMillis = getRefreshTokenLifeTimeInMillisFromConfig(oAuthAppBean);
}
if (log.isDebugEnabled()) {
log.debug("JWT Self Signed Refresh Token Life time set to : " + lifetimeInMillis + "ms.");
log.debug("JWT Self Signed Refresh Token Life time set to : " + refreshTokenValidityPeriodInMillis + "ms.");
}
return refreshTokenValidityPeriodInMillis;
}

private Date getRefreshTokenIssuedTime(OAuthTokenReqMessageContext tokenReqMessageContext,
OAuthAppDO oAuthAppDO, Date currentTime) {

Date refreshTokenIssuedTime = currentTime;
if (tokenReqMessageContext != null &&
tokenReqMessageContext.getProperty(PersistenceConstants.PREV_ACCESS_TOKEN) != null) {
RefreshTokenValidationDataDO validationBean =
(RefreshTokenValidationDataDO) tokenReqMessageContext.getProperty(
PersistenceConstants.PREV_ACCESS_TOKEN);
if (isRenewRefreshToken(oAuthAppDO.getRenewRefreshTokenEnabled()) &&
!OAuthServerConfiguration.getInstance().isExtendRenewedTokenExpiryTimeEnabled()) {
// If refresh token renewal enabled and extend token expiry disabled, set the old token issued and
// validity.
refreshTokenIssuedTime = validationBean.getIssuedTime();
}
if (refreshTokenIssuedTime == null) {
refreshTokenIssuedTime = currentTime;
}
}
return refreshTokenIssuedTime;
}

private boolean isRenewRefreshToken(String renewRefreshToken) {

if (StringUtils.isNotBlank(renewRefreshToken)) {
if (log.isDebugEnabled()) {
log.debug("Reading the Oauth application specific renew " +
"refresh token value as " + renewRefreshToken + " from the IDN_OIDC_PROPERTY table");
}
return Boolean.parseBoolean(renewRefreshToken);
} else {
if (log.isDebugEnabled()) {
log.debug("Reading the global renew refresh token value from the identity.xml");
}
return OAuthServerConfiguration.getInstance().isRefreshTokenRenewalEnabled();
}
return lifetimeInMillis;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ public interface InvalidTokenPersistenceService {
* @param expiryTime The expiry time set for the token during generation.
* @throws IdentityOAuth2Exception If an error occurs while marking the token as invalid.
*/
void addInvalidToken(String token, String consumerKey, Long expiryTime)
throws IdentityOAuth2Exception;
void addInvalidToken(String token, String consumerKey, Long expiryTime) throws IdentityOAuth2Exception;

/**
* Check if a token has been revoked as a result of any changes in the consumer app associated with the token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
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.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.dto.OAuth2AccessTokenReqDTO;
Expand All @@ -34,6 +37,7 @@
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.is.key.manager.tokenpersistence.PersistenceConstants;
import org.wso2.is.key.manager.tokenpersistence.internal.ServiceReferenceHolder;
import org.wso2.is.key.manager.tokenpersistence.utils.TokenMgtUtil;

Expand All @@ -49,15 +53,22 @@
public class InMemoryRefreshTokenGrantProcessor implements RefreshTokenGrantProcessor {

private static final Log log = LogFactory.getLog(InMemoryRefreshTokenGrantProcessor.class);
public static final String PREV_ACCESS_TOKEN = "previousAccessToken";

@Override
public RefreshTokenValidationDataDO validateRefreshToken(OAuthTokenReqMessageContext tokenReqMessageContext)
throws IdentityOAuth2Exception {

OAuth2AccessTokenReqDTO tokenReq = tokenReqMessageContext.getOauth2AccessTokenReqDTO();
return OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
RefreshTokenValidationDataDO validationBean = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
.getVerifiedRefreshToken(tokenReq.getRefreshToken(), tokenReq.getClientId());
if (validationBean == null) {
if (log.isDebugEnabled()) {
log.debug(String.format("Invalid Refresh Token provided for Client with Client Id : %s",
tokenReq.getClientId()));
}
throw new IdentityOAuth2Exception("Valid refresh token data not found");
}
return validationBean;
}

@Override
Expand All @@ -68,12 +79,13 @@ public void persistNewToken(OAuthTokenReqMessageContext tokenReqMessageContext,
String refreshTokenIdentifier;
long tokenExpirationTime;
OAuth2AccessTokenReqDTO tokenReq = tokenReqMessageContext.getOauth2AccessTokenReqDTO();
RefreshTokenValidationDataDO oldAccessToken =
(RefreshTokenValidationDataDO) tokenReqMessageContext.getProperty(PREV_ACCESS_TOKEN);
RefreshTokenValidationDataDO oldRefreshToken =
(RefreshTokenValidationDataDO) tokenReqMessageContext.getProperty(
PersistenceConstants.PREV_ACCESS_TOKEN);
if (!OAuth2Util.isJWT(tokenReq.getRefreshToken())) { // for backward compatibility.
refreshTokenIdentifier = tokenReq.getRefreshToken();
tokenExpirationTime = oldAccessToken.getIssuedTime().getTime()
+ oldAccessToken.getValidityPeriodInMillis();
tokenExpirationTime = oldRefreshToken.getIssuedTime().getTime()
+ oldRefreshToken.getValidityPeriodInMillis();
} else {
SignedJWT signedJWT = SignedJWT.parse(tokenReq.getRefreshToken());
JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
Expand All @@ -88,9 +100,12 @@ public void persistNewToken(OAuthTokenReqMessageContext tokenReqMessageContext,
log.debug("Invalidating previous refresh token.");
}
}
// Make the old refresh token inactive and persist it.
ServiceReferenceHolder.getInstance().getInvalidTokenPersistenceService()
.addInvalidToken(refreshTokenIdentifier, clientId, tokenExpirationTime);
OAuthAppDO oAuthAppDO = TokenMgtUtil.getOAuthApp(tokenReq.getClientId());
if (!isRenewRefreshToken(oAuthAppDO.getRenewRefreshTokenEnabled())) {
// Make the old refresh token inactive and persist it.
ServiceReferenceHolder.getInstance().getInvalidTokenPersistenceService()
.addInvalidToken(refreshTokenIdentifier, clientId, tokenExpirationTime);
}
} catch (ParseException e) {
throw new IdentityOAuth2Exception("Error while parsing refresh token while persisting.", e);
}
Expand Down Expand Up @@ -127,4 +142,26 @@ public boolean isLatestRefreshToken(OAuth2AccessTokenReqDTO tokenReq, RefreshTok

return true;
}

/**
* Evaluate if renew refresh token.
*
* @param renewRefreshToken Renew refresh token config value from OAuthApp.
* @return Evaluated refresh token state
*/
private boolean isRenewRefreshToken(String renewRefreshToken) {

if (StringUtils.isNotBlank(renewRefreshToken)) {
if (log.isDebugEnabled()) {
log.debug("Reading the Oauth application specific renew refresh token value as " + renewRefreshToken
+ " from the IDN_OIDC_PROPERTY table.");
}
return Boolean.parseBoolean(renewRefreshToken);
} else {
if (log.isDebugEnabled()) {
log.debug("Reading the global renew refresh token value from the identity.xml");
}
return OAuthServerConfiguration.getInstance().isRefreshTokenRenewalEnabled();
}
}
}
Loading

0 comments on commit ad5239d

Please sign in to comment.