KEYCLOAK-742 Added always-refresh-token option to adapters
This commit is contained in:
parent
390ca0930b
commit
84e1ace539
9 changed files with 63 additions and 26 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue