Simplify TokenManager expiration calculations using SessionExpirationUtils

Closes https://github.com/keycloak/keycloak/issues/20794
This commit is contained in:
rmartinc 2023-06-12 12:35:00 +02:00 committed by Marek Posolda
parent f90536d6be
commit ecf52285bc
2 changed files with 38 additions and 132 deletions

View file

@ -57,6 +57,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.SessionExpirationUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
@ -93,6 +94,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
@ -974,56 +976,23 @@ public class TokenManager {
}
}
int expiration;
long expiration;
if (tokenLifespan == -1) {
expiration = userSession.getStarted() + (userSession.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0 ?
realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan());
expiration = TimeUnit.SECONDS.toMillis(userSession.getStarted() +
(userSession.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe()
: realm.getSsoSessionMaxLifespan()));
} else {
expiration = Time.currentTime() + tokenLifespan;
expiration = Time.currentTimeMillis() + TimeUnit.SECONDS.toMillis(tokenLifespan);
}
if (userSession.isOffline() || offlineTokenRequested) {
if (realm.isOfflineSessionMaxLifespanEnabled()) {
int sessionExpires = userSession.getStarted() + realm.getOfflineSessionMaxLifespan();
expiration = expiration <= sessionExpires ? expiration : sessionExpires;
long sessionExpires = SessionExpirationUtils.calculateClientSessionMaxLifespanTimestamp(
userSession.isOffline() || offlineTokenRequested, userSession.isRememberMe(),
TimeUnit.SECONDS.toMillis(clientSession.getStarted()), TimeUnit.SECONDS.toMillis(userSession.getStarted()),
realm, client);
expiration = sessionExpires > 0? Math.min(expiration, sessionExpires) : expiration;
int clientOfflineSessionMaxLifespan;
String clientOfflineSessionMaxLifespanPerClient = client
.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN);
if (clientOfflineSessionMaxLifespanPerClient != null
&& !clientOfflineSessionMaxLifespanPerClient.trim().isEmpty()) {
clientOfflineSessionMaxLifespan = Integer.parseInt(clientOfflineSessionMaxLifespanPerClient);
} else {
clientOfflineSessionMaxLifespan = realm.getClientOfflineSessionMaxLifespan();
}
if (clientOfflineSessionMaxLifespan > 0) {
int clientOfflineSessionExpiration = userSession.getStarted() + clientOfflineSessionMaxLifespan;
return expiration < clientOfflineSessionExpiration ? expiration : clientOfflineSessionExpiration;
}
}
} else {
int sessionExpires = userSession.getStarted()
+ (userSession.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe()
: realm.getSsoSessionMaxLifespan());
expiration = expiration <= sessionExpires ? expiration : sessionExpires;
int clientSessionMaxLifespan;
String clientSessionMaxLifespanPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_SESSION_MAX_LIFESPAN);
if (clientSessionMaxLifespanPerClient != null && !clientSessionMaxLifespanPerClient.trim().isEmpty()) {
clientSessionMaxLifespan = Integer.parseInt(clientSessionMaxLifespanPerClient);
} else {
clientSessionMaxLifespan = realm.getClientSessionMaxLifespan();
}
if (clientSessionMaxLifespan > 0) {
int clientSessionExpiration = clientSession.getTimestamp() + clientSessionMaxLifespan;
return expiration < clientSessionExpiration ? expiration : clientSessionExpiration;
}
}
return expiration;
return (int) TimeUnit.MILLISECONDS.toSeconds(expiration);
}
@ -1103,95 +1072,30 @@ public class TokenManager {
refreshToken = new RefreshToken(accessToken);
refreshToken.type(TokenUtil.TOKEN_TYPE_OFFLINE);
if (realm.isOfflineSessionMaxLifespanEnabled())
refreshToken.expiration(getOfflineExpiration());
refreshToken.expiration(getExpiration(true));
sessionManager.createOrUpdateOfflineSession(clientSessionCtx.getClientSession(), userSession);
} else {
refreshToken = new RefreshToken(accessToken);
refreshToken.expiration(getRefreshExpiration());
refreshToken.expiration(getExpiration(false));
}
refreshToken.id(KeycloakModelUtils.generateId());
refreshToken.issuedNow();
return this;
}
private int getRefreshExpiration() {
int sessionExpires = userSession.getStarted()
+ (userSession.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe()
: realm.getSsoSessionMaxLifespan());
private int getExpiration(boolean offline) {
long expiration = SessionExpirationUtils.calculateClientSessionIdleTimestamp(
offline, userSession.isRememberMe(),
TimeUnit.SECONDS.toMillis(clientSessionCtx.getClientSession().getTimestamp()),
realm, client);
long lifespan = SessionExpirationUtils.calculateClientSessionMaxLifespanTimestamp(
offline, userSession.isRememberMe(),
TimeUnit.SECONDS.toMillis(clientSessionCtx.getClientSession().getStarted()),
TimeUnit.SECONDS.toMillis(userSession.getStarted()),
realm, client);
expiration = lifespan > 0? Math.min(expiration, lifespan) : expiration;
int clientSessionMaxLifespan;
String clientSessionMaxLifespanPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_SESSION_MAX_LIFESPAN);
if (clientSessionMaxLifespanPerClient != null && !clientSessionMaxLifespanPerClient.trim().isEmpty()) {
clientSessionMaxLifespan = Integer.parseInt(clientSessionMaxLifespanPerClient);
} else {
clientSessionMaxLifespan = realm.getClientSessionMaxLifespan();
}
if (clientSessionMaxLifespan > 0) {
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
int clientSessionMaxExpiration = clientSession.getStarted() + clientSessionMaxLifespan;
sessionExpires = sessionExpires < clientSessionMaxExpiration ? sessionExpires : clientSessionMaxExpiration;
}
int expiration = Time.currentTime() + (userSession.isRememberMe() && realm.getSsoSessionIdleTimeoutRememberMe() > 0
? realm.getSsoSessionIdleTimeoutRememberMe()
: realm.getSsoSessionIdleTimeout());
int clientSessionIdleTimeout;
String clientSessionIdleTimeoutPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_SESSION_IDLE_TIMEOUT);
if (clientSessionIdleTimeoutPerClient != null && !clientSessionIdleTimeoutPerClient.trim().isEmpty()) {
clientSessionIdleTimeout = Integer.parseInt(clientSessionIdleTimeoutPerClient);
} else {
clientSessionIdleTimeout = realm.getClientSessionIdleTimeout();
}
if (clientSessionIdleTimeout > 0) {
int clientSessionIdleExpiration = Time.currentTime() + clientSessionIdleTimeout;
expiration = expiration < clientSessionIdleExpiration ? expiration : clientSessionIdleExpiration;
}
return expiration <= sessionExpires ? expiration : sessionExpires;
}
private int getOfflineExpiration() {
int sessionExpires = userSession.getStarted() + realm.getOfflineSessionMaxLifespan();
int clientOfflineSessionMaxLifespan;
String clientOfflineSessionMaxLifespanPerClient = client
.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN);
if (clientOfflineSessionMaxLifespanPerClient != null
&& !clientOfflineSessionMaxLifespanPerClient.trim().isEmpty()) {
clientOfflineSessionMaxLifespan = Integer.parseInt(clientOfflineSessionMaxLifespanPerClient);
} else {
clientOfflineSessionMaxLifespan = realm.getClientOfflineSessionMaxLifespan();
}
if (clientOfflineSessionMaxLifespan > 0) {
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
int clientOfflineSessionMaxExpiration = clientSession.getStarted() + clientOfflineSessionMaxLifespan;
sessionExpires = sessionExpires < clientOfflineSessionMaxExpiration ? sessionExpires
: clientOfflineSessionMaxExpiration;
}
int expiration = Time.currentTime() + realm.getOfflineSessionIdleTimeout();
int clientOfflineSessionIdleTimeout;
String clientOfflineSessionIdleTimeoutPerClient = client
.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_IDLE_TIMEOUT);
if (clientOfflineSessionIdleTimeoutPerClient != null
&& !clientOfflineSessionIdleTimeoutPerClient.trim().isEmpty()) {
clientOfflineSessionIdleTimeout = Integer.parseInt(clientOfflineSessionIdleTimeoutPerClient);
} else {
clientOfflineSessionIdleTimeout = realm.getClientOfflineSessionIdleTimeout();
}
if (clientOfflineSessionIdleTimeout > 0) {
int clientOfflineSessionIdleExpiration = Time.currentTime() + clientOfflineSessionIdleTimeout;
expiration = expiration < clientOfflineSessionIdleExpiration ? expiration : clientOfflineSessionIdleExpiration;
}
return expiration <= sessionExpires ? expiration : sessionExpires;
return (int) TimeUnit.MILLISECONDS.toSeconds(expiration);
}
public AccessTokenResponseBuilder generateIDToken() {

View file

@ -1580,20 +1580,22 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
.update()
);
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
assertEquals(200, response.getStatusCode());
assertExpiration(response.getExpiresIn(), 65);
setTimeOffset(70);
getTestingClient().testing().setTestingInfinispanTimeService();
try {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
assertEquals(200, response.getStatusCode());
assertExpiration(response.getExpiresIn(), 65);
setTimeOffset(70);
oauth.openLoginForm();
code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(code, "password");
assertExpiration(response2.getExpiresIn(), 65);
} finally {
getTestingClient().testing().revertTestingInfinispanTimeService();
resetTimeOffset();
}
}