[KEYCLOAK-5844] - Refreshing PAT instead of obtaining a new one every time

This commit is contained in:
Pedro Igor 2017-11-14 11:24:45 -02:00
parent 0f4b8be2b0
commit fdb618219f
6 changed files with 94 additions and 14 deletions

View file

@ -22,12 +22,15 @@ import org.keycloak.authorization.client.resource.AuthorizationResource;
import org.keycloak.authorization.client.resource.EntitlementResource;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.authorization.client.util.Http;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.function.Supplier;
/**
* <p>This is class serves as an entry point for clients looking for access to Keycloak Authorization Services.
@ -37,6 +40,7 @@ import java.net.URI;
public class AuthzClient {
private final Http http;
private Supplier<String> patSupplier;
public static AuthzClient create() {
InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("keycloak.json");
@ -96,7 +100,7 @@ public class AuthzClient {
}
public ProtectionResource protection() {
return new ProtectionResource(this.http, obtainAccessToken().getToken());
return new ProtectionResource(this.http, createPatSupplier());
}
public AuthorizationResource authorization(String accesstoken) {
@ -136,4 +140,40 @@ public class AuthzClient {
public Configuration getConfiguration() {
return this.deployment;
}
private Supplier<String> createPatSupplier() {
if (patSupplier == null) {
patSupplier = new Supplier<String>() {
AccessTokenResponse clientToken = obtainAccessToken();
@Override
public String get() {
String token = clientToken.getToken();
try {
AccessToken accessToken = JsonSerialization.readValue(new JWSInput(token).getContent(), AccessToken.class);
if (accessToken.isActive()) {
return token;
}
clientToken = http.<AccessTokenResponse>post(serverConfiguration.getTokenEndpoint())
.authentication().client()
.form()
.param("grant_type", "refresh_token")
.param("refresh_token", clientToken.getRefreshToken())
.response()
.json(AccessTokenResponse.class)
.execute();
} catch (Exception e) {
patSupplier = null;
throw new RuntimeException(e);
}
return clientToken.getToken();
}
};
}
return patSupplier;
}
}

View file

@ -19,6 +19,8 @@ package org.keycloak.authorization.client.resource;
import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
import java.util.function.Supplier;
import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.representation.PermissionResponse;
import org.keycloak.authorization.client.util.Http;
@ -30,9 +32,9 @@ import org.keycloak.util.JsonSerialization;
public class PermissionResource {
private final Http http;
private final String pat;
private final Supplier<String> pat;
public PermissionResource(Http http, String pat) {
public PermissionResource(Http http, Supplier<String> pat) {
this.http = http;
this.pat = pat;
}
@ -40,7 +42,7 @@ public class PermissionResource {
public PermissionResponse forResource(PermissionRequest request) {
try {
return this.http.<PermissionResponse>post("/authz/protection/permission")
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.json(JsonSerialization.writeValueAsBytes(request))
.response().json(PermissionResponse.class).execute();
} catch (Exception cause) {

View file

@ -20,6 +20,7 @@ package org.keycloak.authorization.client.resource;
import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
import java.util.Set;
import java.util.function.Supplier;
import org.keycloak.authorization.client.representation.RegistrationResponse;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
@ -32,9 +33,9 @@ import org.keycloak.util.JsonSerialization;
public class ProtectedResource {
private final Http http;
private final String pat;
private final Supplier<String> pat;
public ProtectedResource(Http http, String pat) {
public ProtectedResource(Http http, Supplier<String> pat) {
this.http = http;
this.pat = pat;
}
@ -42,7 +43,7 @@ public class ProtectedResource {
public RegistrationResponse create(ResourceRepresentation resource) {
try {
return this.http.<RegistrationResponse>post("/authz/protection/resource_set")
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.json(JsonSerialization.writeValueAsBytes(resource))
.response().json(RegistrationResponse.class).execute();
} catch (Exception cause) {
@ -53,7 +54,7 @@ public class ProtectedResource {
public void update(ResourceRepresentation resource) {
try {
this.http.<RegistrationResponse>put("/authz/protection/resource_set/" + resource.getId())
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.json(JsonSerialization.writeValueAsBytes(resource)).execute();
} catch (Exception cause) {
throw handleAndWrapException("Could not update resource", cause);
@ -63,7 +64,7 @@ public class ProtectedResource {
public RegistrationResponse findById(String id) {
try {
return this.http.<RegistrationResponse>get("/authz/protection/resource_set/" + id)
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.response().json(RegistrationResponse.class).execute();
} catch (Exception cause) {
throw handleAndWrapException("Could not find resource", cause);
@ -73,7 +74,7 @@ public class ProtectedResource {
public Set<String> findByFilter(String filter) {
try {
return this.http.<Set>get("/authz/protection/resource_set")
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.param("filter", filter)
.response().json(Set.class).execute();
} catch (Exception cause) {
@ -84,7 +85,7 @@ public class ProtectedResource {
public Set<String> findAll() {
try {
return this.http.<Set>get("/authz/protection/resource_set")
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.response().json(Set.class).execute();
} catch (Exception cause) {
throw handleAndWrapException("Could not find resource", cause);
@ -94,7 +95,7 @@ public class ProtectedResource {
public void delete(String id) {
try {
this.http.delete("/authz/protection/resource_set/" + id)
.authorizationBearer(this.pat)
.authorizationBearer(this.pat.get())
.execute();
} catch (Exception cause) {
throw handleAndWrapException("Could not delete resource", cause);

View file

@ -17,6 +17,8 @@
*/
package org.keycloak.authorization.client.resource;
import java.util.function.Supplier;
import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
import org.keycloak.authorization.client.util.Http;
@ -25,10 +27,10 @@ import org.keycloak.authorization.client.util.Http;
*/
public class ProtectionResource {
private final String pat;
private final Supplier<String> pat;
private final Http http;
public ProtectionResource(Http http, String pat) {
public ProtectionResource(Http http, Supplier<String> pat) {
if (pat == null) {
throw new RuntimeException("No access token was provided when creating client for Protection API.");
}

View file

@ -52,6 +52,7 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
@ -73,6 +74,7 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
.authenticatorType(JWTClientAuthenticator.PROVIDER_ID))
.build());
testRealms.add(configureRealm(RealmBuilder.create().name("authz-test"), ClientBuilder.create().secret("secret")).build());
testRealms.add(configureRealm(RealmBuilder.create().name("authz-test-session").accessTokenLifespan(1), ClientBuilder.create().secret("secret")).build());
}
@Before
@ -168,6 +170,31 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
assertAccessProtectionAPI(protection);
}
@Test
public void testReusingAccessAndRefreshTokens() throws Exception {
ClientsResource clients = getAdminClient().realm("authz-test-session").clients();
ClientRepresentation clientRepresentation = clients.findByClientId("resource-server-test").get(0);
List<UserSessionRepresentation> userSessions = clients.get(clientRepresentation.getId()).getUserSessions(-1, -1);
assertEquals(0, userSessions.size());
AuthzClient authzClient = getAuthzClient("default-session-keycloak.json");
ProtectionResource protection = authzClient.protection();
protection.resource().findByFilter("name=Default Resource");
userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
assertEquals(1, userSessions.size());
Thread.sleep(2000);
protection = authzClient.protection();
protection.resource().findByFilter("name=Default Resource");
userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
assertEquals(1, userSessions.size());
}
private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) {
return builder
.roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false)))

View file

@ -0,0 +1,8 @@
{
"realm": "authz-test-session",
"auth-server-url" : "http://localhost:8180/auth",
"resource" : "resource-server-test",
"credentials": {
"secret": "secret"
}
}