KEYCLOAK-742 Added always-refresh-token option to adapters

This commit is contained in:
mposolda 2014-10-05 22:12:54 +02:00
parent 390ca0930b
commit 84e1ace539
9 changed files with 63 additions and 26 deletions

View file

@ -17,7 +17,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
"connection-pool-size",
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
"client-keystore", "client-keystore-password", "client-key-password",
"auth-server-url-for-backend-requests"
"auth-server-url-for-backend-requests", "always-refresh-token"
})
public class AdapterConfig extends BaseAdapterConfig {
@ -39,6 +39,8 @@ public class AdapterConfig extends BaseAdapterConfig {
protected int connectionPoolSize = 20;
@JsonProperty("auth-server-url-for-backend-requests")
protected String authServerUrlForBackendRequests;
@JsonProperty("always-refresh-token")
protected boolean alwaysRefreshToken = false;
public boolean isAllowAnyHostname() {
return allowAnyHostname;
@ -111,4 +113,12 @@ public class AdapterConfig extends BaseAdapterConfig {
public void setAuthServerUrlForBackendRequests(String authServerUrlForBackendRequests) {
this.authServerUrlForBackendRequests = authServerUrlForBackendRequests;
}
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
public void setAlwaysRefreshToken(boolean alwaysRefreshToken) {
this.alwaysRefreshToken = alwaysRefreshToken;
}
}

View file

@ -326,6 +326,16 @@ public class AdapterDeploymentContext {
public void setCorsAllowedHeaders(String corsAllowedHeaders) {
delegate.setCorsAllowedHeaders(corsAllowedHeaders);
}
@Override
public boolean isAlwaysRefreshToken() {
return delegate.isAlwaysRefreshToken();
}
@Override
public void setAlwaysRefreshToken(boolean alwaysRefreshToken) {
delegate.setAlwaysRefreshToken(alwaysRefreshToken);
}
}
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {

View file

@ -47,6 +47,7 @@ public class KeycloakDeployment {
protected String corsAllowedHeaders;
protected String corsAllowedMethods;
protected boolean exposeToken;
protected boolean alwaysRefreshToken;
protected volatile int notBefore;
public KeycloakDeployment() {
@ -281,4 +282,11 @@ public class KeycloakDeployment {
this.notBefore = notBefore;
}
public boolean isAlwaysRefreshToken() {
return alwaysRefreshToken;
}
public void setAlwaysRefreshToken(boolean alwaysRefreshToken) {
this.alwaysRefreshToken = alwaysRefreshToken;
}
}

View file

@ -35,7 +35,7 @@ public class KeycloakDeploymentBuilder {
String realmKeyPem = adapterConfig.getRealmKey();
if (realmKeyPem != null) {
PublicKey realmKey = null;
PublicKey realmKey;
try {
realmKey = PemUtils.decodePublicKey(realmKeyPem);
} catch (Exception e) {
@ -60,9 +60,7 @@ public class KeycloakDeploymentBuilder {
}
deployment.setBearerOnly(adapterConfig.isBearerOnly());
if (adapterConfig.isBearerOnly()) {
}
deployment.setAlwaysRefreshToken(adapterConfig.isAlwaysRefreshToken());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
@ -82,7 +80,7 @@ public class KeycloakDeploymentBuilder {
public static KeycloakDeployment build(InputStream is) {
ObjectMapper mapper = new ObjectMapper(new SystemPropertiesJsonParserFactory());
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
AdapterConfig adapterConfig = null;
AdapterConfig adapterConfig;
try {
adapterConfig = mapper.readValue(is, AdapterConfig.class);
} catch (IOException e) {

View file

@ -32,13 +32,13 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
@Override
public AccessToken getToken() {
refreshExpiredToken();
refreshExpiredToken(true);
return super.getToken();
}
@Override
public String getTokenString() {
refreshExpiredToken();
refreshExpiredToken(true);
return super.getTokenString();
}
@ -62,12 +62,19 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
this.deployment = deployment;
}
public void refreshExpiredToken() {
if (log.isTraceEnabled()) {
log.trace("checking whether to refresh.");
/**
* @param checkActive if true, then we won't send refresh request if current accessToken is still active.
* @return true if accessToken is active or was successfully refreshed
*/
public boolean refreshExpiredToken(boolean checkActive) {
if (checkActive) {
if (log.isTraceEnabled()) {
log.trace("checking whether to refresh.");
}
if (isActive()) return true;
}
if (isActive()) return;
if (this.deployment == null || refreshToken == null) return; // Might be serialized in HttpSession?
if (this.deployment == null || refreshToken == null) return false; // Might be serialized in HttpSession?
if (log.isTraceEnabled()) {
log.trace("Doing refresh");
@ -77,10 +84,10 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
response = ServerRequest.invokeRefresh(deployment, refreshToken);
} catch (IOException e) {
log.error("Refresh token failure", e);
return;
return false;
} catch (ServerRequest.HttpFailure httpFailure) {
log.error("Refresh token failure status: " + httpFailure.getStatus() + " " + httpFailure.getError());
return;
return false;
}
if (log.isTraceEnabled()) {
log.trace("received refresh response");
@ -100,7 +107,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
this.token = token;
this.refreshToken = response.getRefreshToken();
this.tokenString = tokenString;
return true;
}

View file

@ -175,12 +175,12 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (session == null) return;
// just in case session got serialized
if (session.getDeployment() == null) session.setDeployment(deploymentContext.resolveDeployment(facade));
if (session.isActive()) return;
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
session.refreshExpiredToken();
if (session.isActive()) return;
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) return;
request.getSessionInternal().removeNote(KeycloakSecurityContext.class.getName());
request.setUserPrincipal(null);

View file

@ -177,12 +177,12 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
if (session == null) return;
// just in case session got serialized
if (session.getDeployment() == null) session.setDeployment(deploymentContext.resolveDeployment(facade));
if (session.isActive()) return;
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
session.refreshExpiredToken();
if (session.isActive()) return;
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) return;
request.getSessionInternal().removeNote(KeycloakSecurityContext.class.getName());
request.setUserPrincipal(null);

View file

@ -92,14 +92,14 @@ public class KeycloakUndertowAccount implements Account, Serializable, KeycloakA
public boolean isActive() {
// this object may have been serialized, so we need to reset realm config/metadata
RefreshableKeycloakSecurityContext session = getKeycloakSecurityContext();
if (session.isActive()) {
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) {
log.debug("session is active");
return true;
}
log.debug("session is not active try refresh");
session.refreshExpiredToken();
if (!session.isActive()) {
log.debug("session is not active or refresh is enforced. Try refresh");
boolean success = session.refreshExpiredToken(false);
if (!success || !session.isActive()) {
log.debug("session is not active return with failure");
return false;

View file

@ -23,6 +23,10 @@ done;
# Configure admin-access.war
sed -i -e 's/false/true/' admin-access.war/WEB-INF/web.xml
# Enforce refreshing token for product-portal and customer-portal war
sed -i -e 's/\"\/auth\",/&\n \"always-refresh-token\": true,/' customer-portal.war/WEB-INF/keycloak.json;
sed -i -e 's/\"\/auth\",/&\n \"always-refresh-token\": true,/' product-portal.war/WEB-INF/keycloak.json;
# Configure other examples
for I in *.war/WEB-INF/keycloak.json; do
sed -i -e 's/\"\/auth\",/&\n \"auth-server-url-for-backend-requests\": \"http:\/\/\$\{jboss.host.name\}:8080\/auth\",/' $I;