SessionExpirationUtils.java
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.utils;
import java.util.concurrent.TimeUnit;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.utils.StringUtil;
/**
* <p>Shared methods to calculate the session expiration and idle.</p>
*
* @author rmartinc
*/
public class SessionExpirationUtils {
/**
* Calculates the time in which the session is expired via max lifetime
* configuration.
* @param offline is the session offline?
* @param isRememberMe is the session remember me?
* @param created timestamp when the session was created
* @param realm The realm model
* @return The time when the user session is expired or -1 if does not expire
*/
public static long calculateUserSessionMaxLifespanTimestamp(boolean offline, boolean isRememberMe, long created, RealmModel realm) {
long timestamp = -1;
if (offline) {
if (realm.isOfflineSessionMaxLifespanEnabled()) {
timestamp = created + TimeUnit.SECONDS.toMillis(getOfflineSessionMaxLifespan(realm));
}
} else {
long userSessionMaxLifespan = TimeUnit.SECONDS.toMillis(getSsoSessionMaxLifespan(realm));
if (isRememberMe) {
userSessionMaxLifespan = Math.max(userSessionMaxLifespan, TimeUnit.SECONDS.toMillis(realm.getSsoSessionMaxLifespanRememberMe()));
}
timestamp = created + userSessionMaxLifespan;
}
return timestamp;
}
/**
* Calculates the time in which the user session is expired via the idle
* configuration.
* @param offline is the session offline?
* @param isRememberMe is the session remember me?
* @param lastRefreshed The last time the session was refreshed
* @param realm The realm model
* @return The time in which the user session is expired by idle timeout
*/
public static long calculateUserSessionIdleTimestamp(boolean offline, boolean isRememberMe, long lastRefreshed, RealmModel realm) {
long timestamp;
if (offline) {
timestamp = lastRefreshed + TimeUnit.SECONDS.toMillis(getOfflineSessionIdleTimeout(realm));
} else {
long userSessionIdleTimeout = TimeUnit.SECONDS.toMillis(getSsoSessionIdleTimeout(realm));
if (isRememberMe) {
userSessionIdleTimeout = Math.max(userSessionIdleTimeout, TimeUnit.SECONDS.toMillis(realm.getSsoSessionIdleTimeoutRememberMe()));
}
timestamp = lastRefreshed + userSessionIdleTimeout;
}
return timestamp;
}
/**
* Calculates the time in which the client session is expired via lifespan
* configuration in the realm and client.
* @param offline is the session offline?
* @param isRememberMe is the session remember me?
* @param clientSessionCreated timestamp when the client session was created
* @param userSessionCreated timestamp when the user session was created
* @param realm The realm model
* @param client The client model
* @return The time when the client session is expired or -1 if does not expire
*/
public static long calculateClientSessionMaxLifespanTimestamp(boolean offline, boolean isRememberMe,
long clientSessionCreated, long userSessionCreated, RealmModel realm, ClientModel client) {
long timestamp = -1;
if (offline) {
long clientOfflineSessionMaxLifespan = getClientAttributeTimeout(client, OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN);
if (realm.isOfflineSessionMaxLifespanEnabled() || clientOfflineSessionMaxLifespan > 0) {
if (clientOfflineSessionMaxLifespan > 0) {
clientOfflineSessionMaxLifespan = TimeUnit.SECONDS.toMillis(clientOfflineSessionMaxLifespan);
} else if (realm.getClientOfflineSessionMaxLifespan() > 0) {
clientOfflineSessionMaxLifespan = TimeUnit.SECONDS.toMillis(realm.getClientOfflineSessionMaxLifespan());
} else {
clientOfflineSessionMaxLifespan = TimeUnit.SECONDS.toMillis(getOfflineSessionMaxLifespan(realm));
}
timestamp = clientSessionCreated + clientOfflineSessionMaxLifespan;
long userSessionExpires = calculateUserSessionMaxLifespanTimestamp(offline, isRememberMe, userSessionCreated, realm);
timestamp = userSessionExpires > 0? Math.min(timestamp, userSessionExpires) : timestamp;
}
} else {
long clientSessionMaxLifespan = TimeUnit.SECONDS.toMillis(getSsoSessionMaxLifespan(realm));
if (isRememberMe) {
clientSessionMaxLifespan = Math.max(clientSessionMaxLifespan, TimeUnit.SECONDS.toMillis(realm.getSsoSessionMaxLifespanRememberMe()));
}
long clientSessionMaxLifespanPerClient = getClientAttributeTimeout(client, OIDCConfigAttributes.CLIENT_SESSION_MAX_LIFESPAN);
if (clientSessionMaxLifespanPerClient > 0) {
clientSessionMaxLifespan = TimeUnit.SECONDS.toMillis(clientSessionMaxLifespanPerClient);
} else if (realm.getClientSessionMaxLifespan() > 0) {
clientSessionMaxLifespan = TimeUnit.SECONDS.toMillis(realm.getClientSessionMaxLifespan());
}
timestamp = clientSessionCreated + clientSessionMaxLifespan;
long userSessionExpires = calculateUserSessionMaxLifespanTimestamp(offline, isRememberMe, userSessionCreated, realm);
timestamp = Math.min(timestamp, userSessionExpires);
}
return timestamp;
}
/**
* Calculates the time in which the user session is expired via the idle
* configuration in the realm and client.
* @param offline is the session offline?
* @param isRememberMe is the session remember me?
* @param lastRefreshed the last time the client session was refreshed
* @param realm the realm model
* @param client the client model
* @return The time in which the client session is expired by idle timeout
*/
public static long calculateClientSessionIdleTimestamp(boolean offline, boolean isRememberMe, long lastRefreshed,
RealmModel realm, ClientModel client) {
long timestamp;
if (offline) {
long clientOfflineSessionIdleTimeout = TimeUnit.SECONDS.toMillis(getOfflineSessionIdleTimeout(realm));
long clientOfflineSessionIdleTimeoutPerClient = getClientAttributeTimeout(client, OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_IDLE_TIMEOUT);
if (clientOfflineSessionIdleTimeoutPerClient > 0) {
clientOfflineSessionIdleTimeout = TimeUnit.SECONDS.toMillis(clientOfflineSessionIdleTimeoutPerClient);
} else if (realm.getClientOfflineSessionIdleTimeout() > 0) {
clientOfflineSessionIdleTimeout = TimeUnit.SECONDS.toMillis(realm.getClientOfflineSessionIdleTimeout());
}
timestamp = lastRefreshed + clientOfflineSessionIdleTimeout;
} else {
long clientSessionIdleTimeout = TimeUnit.SECONDS.toMillis(getSsoSessionIdleTimeout(realm));
if (isRememberMe) {
clientSessionIdleTimeout = Math.max(clientSessionIdleTimeout, TimeUnit.SECONDS.toMillis(realm.getSsoSessionIdleTimeoutRememberMe()));
}
long clientSessionIdleTimeoutPerClient = getClientAttributeTimeout(client, OIDCConfigAttributes.CLIENT_SESSION_IDLE_TIMEOUT);
if (clientSessionIdleTimeoutPerClient > 0) {
clientSessionIdleTimeout = TimeUnit.SECONDS.toMillis(clientSessionIdleTimeoutPerClient);
} else if (realm.getClientSessionIdleTimeout() > 0){
clientSessionIdleTimeout = TimeUnit.SECONDS.toMillis(realm.getClientSessionIdleTimeout());
}
timestamp = lastRefreshed + clientSessionIdleTimeout;
}
return timestamp;
}
private static int getSsoSessionMaxLifespan(RealmModel realm) {
int lifespan = realm.getSsoSessionMaxLifespan();
if (lifespan <= 0) {
lifespan = Constants.DEFAULT_SESSION_MAX_LIFESPAN;
}
return lifespan;
}
private static int getOfflineSessionMaxLifespan(RealmModel realm) {
int lifespan = realm.getOfflineSessionMaxLifespan();
if (lifespan <= 0) {
lifespan = Constants.DEFAULT_OFFLINE_SESSION_MAX_LIFESPAN;
}
return lifespan;
}
private static int getSsoSessionIdleTimeout(RealmModel realm) {
int idle = realm.getSsoSessionIdleTimeout();
if (idle <= 0) {
idle = Constants.DEFAULT_SESSION_IDLE_TIMEOUT;
}
return idle;
}
private static int getOfflineSessionIdleTimeout(RealmModel realm) {
int idle = realm.getOfflineSessionIdleTimeout();
if (idle <= 0) {
idle = Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT;
}
return idle;
}
private static long getClientAttributeTimeout(ClientModel client, String attr) {
if (client != null) {
final String value = client.getAttribute(attr);
if (StringUtil.isNotBlank(value)) {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
// no-op
}
}
}
return -1;
}
}