[KEYCLOAK-5844] - Refreshing PAT instead of obtaining a new one every time
This commit is contained in:
parent
0f4b8be2b0
commit
fdb618219f
6 changed files with 94 additions and 14 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"realm": "authz-test-session",
|
||||
"auth-server-url" : "http://localhost:8180/auth",
|
||||
"resource" : "resource-server-test",
|
||||
"credentials": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue