diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index 92655e0..52c5561 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -46,7 +46,7 @@ public class Keycloak {
Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, ResteasyClient resteasyClient){
config = new Config(serverUrl, realm, username, password, clientId, clientSecret);
- client = resteasyClient != null ? resteasyClient : new ResteasyClientBuilder().build();
+ client = resteasyClient != null ? resteasyClient : new ResteasyClientBuilder().connectionPoolSize(10).build();
tokenManager = new TokenManager(config, client);
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
index ff5315b..514aeea 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
@@ -21,14 +21,18 @@ import org.keycloak.admin.client.token.TokenManager;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.core.HttpHeaders;
import java.io.IOException;
+import java.util.List;
/**
* @author rodrigo.sasaki@icarros.com.br
*/
-public class BearerAuthFilter implements ClientRequestFilter {
+public class BearerAuthFilter implements ClientRequestFilter, ClientResponseFilter {
+ public static final String AUTH_HEADER_PREFIX = "Bearer ";
private final String tokenString;
private final TokenManager tokenManager;
@@ -45,9 +49,27 @@ public class BearerAuthFilter implements ClientRequestFilter {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
- String authHeader = "Bearer " + (tokenManager != null ? tokenManager.getAccessTokenString() : tokenString);
+ String authHeader = AUTH_HEADER_PREFIX + (tokenManager != null ? tokenManager.getAccessTokenString() : tokenString);
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
}
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+ if (responseContext.getStatus() == 401 && tokenManager != null) {
+ List<Object> authHeaders = requestContext.getHeaders().get(HttpHeaders.AUTHORIZATION);
+ if (authHeaders == null) {
+ return;
+ }
+ for (Object authHeader : authHeaders) {
+ if (authHeader instanceof String) {
+ String headerValue = (String) authHeader;
+ if (headerValue.startsWith(AUTH_HEADER_PREFIX)) {
+ String token = headerValue.substring( AUTH_HEADER_PREFIX.length() );
+ tokenManager.invalidate( token );
+ }
+ }
+ }
+ }
+ }
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
index 02056a7..e325681 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
@@ -18,7 +18,6 @@
package org.keycloak.admin.client.token;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
-import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.keycloak.admin.client.Config;
import org.keycloak.admin.client.resource.BasicAuthFilter;
@@ -27,8 +26,6 @@ import org.keycloak.representations.AccessTokenResponse;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.core.Form;
-import java.util.Calendar;
-import java.util.Date;
/**
* @author rodrigo.sasaki@icarros.com.br
@@ -41,18 +38,22 @@ public class TokenManager {
private long expirationTime;
private long minTokenValidity = DEFAULT_MIN_VALIDITY;
private final Config config;
- private final ResteasyClient client;
+ private final TokenService tokenService;
public TokenManager(Config config, ResteasyClient client){
this.config = config;
- this.client = client;
+ ResteasyWebTarget target = client.target(config.getServerUrl());
+ if(!config.isPublicClient()){
+ target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
+ }
+ tokenService = target.proxy(TokenService.class);
}
public String getAccessTokenString(){
return getAccessToken().getToken();
}
- public AccessTokenResponse getAccessToken(){
+ public synchronized AccessTokenResponse getAccessToken(){
if(currentToken == null){
grantToken();
}else if(tokenExpired()){
@@ -62,8 +63,6 @@ public class TokenManager {
}
public AccessTokenResponse grantToken(){
- ResteasyWebTarget target = client.target(config.getServerUrl());
-
Form form = new Form()
.param("grant_type", "password")
.param("username", config.getUsername())
@@ -71,51 +70,58 @@ public class TokenManager {
if(config.isPublicClient()){
form.param("client_id", config.getClientId());
- } else {
- target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
}
- TokenService tokenService = target.proxy(TokenService.class);
-
int requestTime = Time.currentTime();
- currentToken = tokenService.grantToken(config.getRealm(), form.asMap());
- expirationTime = requestTime + currentToken.getExpiresIn();
-
+ synchronized (this) {
+ currentToken = tokenService.grantToken( config.getRealm(), form.asMap() );
+ expirationTime = requestTime + currentToken.getExpiresIn();
+ }
return currentToken;
}
public AccessTokenResponse refreshToken(){
- ResteasyWebTarget target = client.target(config.getServerUrl());
-
Form form = new Form()
.param("grant_type", "refresh_token")
.param("refresh_token", currentToken.getRefreshToken());
if(config.isPublicClient()){
form.param("client_id", config.getClientId());
- } else {
- target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
}
- TokenService tokenService = target.proxy(TokenService.class);
-
try {
int requestTime = Time.currentTime();
- currentToken = tokenService.refreshToken(config.getRealm(), form.asMap());
- expirationTime = requestTime + currentToken.getExpiresIn();
+ synchronized (this) {
+ currentToken = tokenService.refreshToken( config.getRealm(), form.asMap() );
+ expirationTime = requestTime + currentToken.getExpiresIn();
+ }
return currentToken;
} catch (BadRequestException e) {
return grantToken();
}
}
- public void setMinTokenValidity(long minTokenValidity) {
+ public synchronized void setMinTokenValidity(long minTokenValidity) {
this.minTokenValidity = minTokenValidity;
}
- private boolean tokenExpired() {
+ private synchronized boolean tokenExpired() {
return (Time.currentTime() + minTokenValidity) >= expirationTime;
}
+ /**
+ * Invalidates the current token, but only when it is equal to the token passed as an argument.
+ *
+ * @param token the token to invalidate (cannot be null).
+ */
+ public void invalidate(String token) {
+ if (currentToken == null) {
+ return; // There's nothing to invalidate.
+ }
+ if (token.equals(currentToken.getToken())) {
+ // When used next, this cause a refresh attempt, that in turn will cause a grant attempt if refreshing fails.
+ expirationTime = -1;
+ }
+ }
}