[#22515] Added refresh token methods

This commit is contained in:
Mauro Mugnaini 2021-12-09 15:05:26 +01:00
parent 21774d9a91
commit f5ef1d2c92
9 changed files with 366 additions and 56 deletions

View File

@ -2,5 +2,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "keycloak-client"
## [v1.1.0-SNAPSHOT]
## [v1.0.1]
- First release (#21389 #22155) provides the basic helper classes for Keycloak tokens retrieve and functions for the gCube framework integration (automatic service discovery).

View File

@ -13,7 +13,7 @@
<groupId>org.gcube.common</groupId>
<artifactId>keycloak-client</artifactId>
<version>1.0.1</version>
<version>1.1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>

View File

@ -2,6 +2,7 @@ package org.gcube.common.keycloak;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import static org.gcube.common.keycloak.model.OIDCConstants.*;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
@ -17,6 +18,7 @@ import java.util.stream.Collectors;
import org.gcube.common.gxrest.request.GXHTTPStringRequest;
import org.gcube.common.gxrest.response.inbound.GXInboundResponse;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
@ -26,11 +28,6 @@ import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
public class DefaultKeycloakClient implements KeycloakClient {
private static final String PERMISSION_PARAMETER = "permission";
private static final String GRANT_TYPE_PARAMETER = "grant_type";
private static final String UMA_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
private static final String AUDIENCE_PARAMETER = "audience";
@Override
public URL findTokenEndpointURL() throws KeycloakClientException {
logger.debug("Checking ScopeProvider's scope presence and format");
@ -102,41 +99,38 @@ public class DefaultKeycloakClient implements KeycloakClient {
List<String> permissions) throws KeycloakClientException {
if (tokenURL == null) {
throw new KeycloakClientException("'tokenURL' parameter must be not null");
throw new KeycloakClientException("Token URL must be not null");
}
if (authorization == null || "".equals(authorization)) {
throw new KeycloakClientException("'authorization' parameter must be not null nor empty");
throw new KeycloakClientException("Authorization must be not null nor empty");
}
if (audience == null || "".equals(audience)) {
throw new KeycloakClientException("'audience' parameter must be not null nor empty");
throw new KeycloakClientException("Audience must be not null nor empty");
}
logger.debug("Querying token from Keycloak server with URL: {}", tokenURL);
Map<String, List<String>> params = new HashMap<>();
params.put(GRANT_TYPE_PARAMETER, Arrays.asList(UMA_TOKEN_GRANT_TYPE));
try {
params.put(AUDIENCE_PARAMETER, Arrays.asList(URLEncoder.encode(checkAudience(audience), "UTF-8")));
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
if (permissions != null && !permissions.isEmpty()) {
params.put(
PERMISSION_PARAMETER, permissions.stream().map(s -> {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}).collect(Collectors.toList()));
}
// Constructing request object
GXHTTPStringRequest request;
try {
Map<String, List<String>> params = new HashMap<>();
params.put(GRANT_TYPE_PARAMETER, Arrays.asList(UMA_TOKEN_GRANT_TYPE));
params.put(AUDIENCE_PARAMETER, Arrays.asList(URLEncoder.encode(checkAudience(audience), "UTF-8")));
if (permissions != null && !permissions.isEmpty()) {
params.put(
PERMISSION_PARAMETER, permissions.stream().map(s -> {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}).collect(Collectors.toList()));
}
String queryString = params.entrySet().stream()
.flatMap(p -> p.getValue().stream().map(v -> p.getKey() + "=" + v))
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
@ -168,7 +162,7 @@ public class DefaultKeycloakClient implements KeycloakClient {
} else {
throw KeycloakClientException.create("Unable to get token", response.getHTTPCode(),
response.getHeaderFields()
.getOrDefault("Content-Type", Collections.singletonList("unknown/unknown")).get(0),
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
response.getMessage());
}
}
@ -185,4 +179,113 @@ public class DefaultKeycloakClient implements KeycloakClient {
return audience;
}
@Override
public TokenResponse refreshToken(TokenResponse tokenResponse) throws KeycloakClientException {
return refreshToken((String) null, tokenResponse);
}
@Override
public TokenResponse refreshToken(URL tokenURL, TokenResponse tokenResponse) throws KeycloakClientException {
return refreshToken(tokenURL, null, null, tokenResponse);
}
@Override
public TokenResponse refreshToken(String clientId, TokenResponse tokenResponse) throws KeycloakClientException {
return refreshToken(clientId, null, tokenResponse);
}
@Override
public TokenResponse refreshToken(String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException {
return refreshToken(findTokenEndpointURL(), clientId, clientSecret, tokenResponse);
}
@Override
public TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException {
if (clientId == null) {
logger.debug("Client id not set, trying to get it from access token info");
try {
clientId = ModelUtils.getClientIdFromToken(ModelUtils.getAccessTokenFrom(tokenResponse));
} catch (Exception e) {
throw new KeycloakClientException("Cannot construct access token object from token response", e);
}
}
return refreshToken(tokenURL, clientId, clientSecret, tokenResponse.getRefreshToken());
}
@Override
public TokenResponse refreshToken(String clientId, String refreshTokenJWTString) throws KeycloakClientException {
return refreshToken(clientId, null, refreshTokenJWTString);
}
@Override
public TokenResponse refreshToken(String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException {
return refreshToken(findTokenEndpointURL(), clientId, clientSecret, refreshTokenJWTString);
}
@Override
public TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException {
if (tokenURL == null) {
throw new KeycloakClientException("Token URL must be not null");
}
if (clientId == null || "".equals(clientId)) {
throw new KeycloakClientException("Client id must be not null nor empty");
}
if (refreshTokenJWTString == null || "".equals(clientId)) {
throw new KeycloakClientException("Refresh token JWT encoded string must be not null nor empty");
}
logger.debug("Refreshing token from Keycloak server with URL: {}", tokenURL);
// Constructing request object
GXHTTPStringRequest request;
try {
Map<String, String> params = new HashMap<>();
params.put(GRANT_TYPE_PARAMETER, REFRESH_TOKEN_GRANT_TYPE);
params.put(REFRESH_TOKEN_PARAMETER, refreshTokenJWTString);
params.put(CLIENT_ID_PARAMETER, URLEncoder.encode(clientId, "UTF-8"));
if (clientSecret != null && !"".equals(clientSecret)) {
params.put(CLIENT_SECRET_PARAMETER, URLEncoder.encode(clientSecret, "UTF-8"));
}
String queryString = params.entrySet().stream()
.map(p -> p.getKey() + "=" + p.getValue())
.reduce((p1, p2) -> p1 + "&" + p2).orElse("");
request = GXHTTPStringRequest.newRequest(tokenURL.toString()).header("Content-Type",
"application/x-www-form-urlencoded").withBody(queryString);
request.isExternalCall(true);
} catch (Exception e) {
throw new KeycloakClientException("Cannot construct the request object correctly", e);
}
GXInboundResponse response;
try {
response = request.post();
} catch (Exception e) {
throw new KeycloakClientException("Cannot send request correctly", e);
}
if (response.isSuccessResponse()) {
try {
return response.tryConvertStreamedContentFromJson(TokenResponse.class);
} catch (Exception e) {
throw new KeycloakClientException("Cannot construct token response object correctly", e);
}
} else {
throw KeycloakClientException.create("Unable to get token", response.getHTTPCode(),
response.getHeaderFields()
.getOrDefault("content-type", Collections.singletonList("unknown/unknown")).get(0),
response.getMessage());
}
}
}

View File

@ -18,6 +18,7 @@ public interface KeycloakClient {
/**
* Finds the keycloak endpoint {@link URL} discovering it in the current scope provided by {@link ScopeProvider}
*
* @return the keycloak endpoint URL in the current scope
* @throws KeycloakClientException if something goes wrong discovering the endpoint URL
*/
@ -54,7 +55,7 @@ public interface KeycloakClient {
throws KeycloakClientException;
/**
* Queries an UMA token from the discovered Keycloak server in the current scope, by using provided clientId and client secret
* Queries an UMA token from the Keycloak server discovered in the current scope, by using provided clientId and client secret
* for the given audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param clientId the client id
@ -68,7 +69,7 @@ public interface KeycloakClient {
throws KeycloakClientException;
/**
* Queries an UMA token from the discovered Keycloak server in the current scope, by using provided clientId and client secret
* Queries an UMA token from the Keycloak server discovered in the current scope, by using provided clientId and client secret
* for the current scope audience (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param clientId the client id
@ -79,4 +80,114 @@ public interface KeycloakClient {
*/
TokenResponse queryUMAToken(String clientId, String clientSecret, List<String> permissions)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the refresh
* token JWT encoded string in the token response object.
*
* Client id will be read from "issued for" access token's claim and client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object.
*
* Client id will be read from "issued for" access token's claim and client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(URL tokenURL, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the refresh
* token JWT encoded string in the token response object and the provided client id.
*
* Client secret will be not sent.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String clientId, TokenResponse tokenResponse) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the refresh
* token JWT encoded string in the token response object and the provided client id and secret.
*
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server using the refresh token JWT encoded string in the
* token response object and the provided client id and secret.
*
* @param clientId the requestor client id, may be <code>null</code> and in this case will be take from the access token "issued for" claim
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param tokenResponse the previously issued token as {@link TokenResponse} object
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, TokenResponse tokenResponse)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the provided
* client id and the refresh token JWT encoded string obtained with the access token in the previous token response.
*
* Client secret will be not used.
* <br><b>NOTE</b>: For <code>public</code> clients types only.
*
* @param clientId the requestor client id
* @param refreshTokenJWTString the previously issued refresh token JWT string taken from the same token response of the access token parameter
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String clientId, String refreshTokenJWTString) throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server discovered in the current scope using the provided
* client id and secret and the refresh token JWT encoded string obtained with the access token in the previous
* token response.
*
* @param clientId the requestor client id
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param refreshTokenJWTString the previously issued refresh token JWT string taken from the same token response of the access token parameter
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException;
/**
* Refreshes a previously issued token from the Keycloak server by using the client id and secret
* and the refresh token JWT encoded string obtained with the access token in the previous token response.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the requestor client id
* @param clientSecret the requestor client secret, may be <code>null</code> for non-confidential clients
* @param refreshTokenJWTString the previously issued refresh token JWT string
* @return the refreshed token as {@link TokenResponse} object
* @throws KeycloakClientException if something goes wrong performing the refresh query
*/
TokenResponse refreshToken(URL tokenURL, String clientId, String clientSecret, String refreshTokenJWTString)
throws KeycloakClientException;
}

View File

@ -35,7 +35,7 @@ public class KeycloakClientException extends Exception {
super(message);
}
public KeycloakClientException(String message, Exception cause) {
public KeycloakClientException(String message, Throwable cause) {
super(message, cause);
}

View File

@ -1,6 +1,8 @@
package org.gcube.common.keycloak.model;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.gcube.com.fasterxml.jackson.core.JsonProcessingException;
@ -13,6 +15,8 @@ public class ModelUtils {
protected static final Logger logger = LoggerFactory.getLogger(ModelUtils.class);
private static final String ACCOUNT_AUDIENCE_RESOURCE = "account";
private static final ObjectMapper mapper = new ObjectMapper();
static {
@ -33,31 +37,40 @@ public class ModelUtils {
}
}
private static byte[] getDecodedPayload(String value) {
return getBase64Decoded(getEncodedPayload(value));
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse) throws Exception {
return getAccessTokenPayloadJSONStringFrom(tokenResponse, true);
}
public static String getAccessTokenPayloadStringFrom(TokenResponse tokenResponse) throws Exception {
return getAccessTokenPayloadStringFrom(tokenResponse, true);
}
public static String getAccessTokenPayloadStringFrom(TokenResponse tokenResponse, boolean prettyPrint) throws Exception {
public static String getAccessTokenPayloadJSONStringFrom(TokenResponse tokenResponse, boolean prettyPrint)
throws Exception {
return toJSONString(getAccessTokenFrom(tokenResponse, Object.class), prettyPrint);
}
public static AccessToken getAccessTokenFrom(TokenResponse tokenResponse) throws Exception {
return getAccessTokenFrom(tokenResponse, RefreshToken.class);
return getAccessTokenFrom(tokenResponse, AccessToken.class);
}
public static AccessToken getAccessTokenFrom(String authorizationHeaderOrBase64EncodedJWT) throws Exception {
return getAccessTokenFrom(authorizationHeaderOrBase64EncodedJWT.matches("[b|B]earer ")
? authorizationHeaderOrBase64EncodedJWT.substring("bearer ".length())
: authorizationHeaderOrBase64EncodedJWT, AccessToken.class);
}
private static <T> T getAccessTokenFrom(TokenResponse tokenResponse, Class<T> clazz) throws Exception {
return mapper.readValue(getDecodedPayload(tokenResponse.getAccessToken()), clazz);
return getAccessTokenFrom(tokenResponse.getAccessToken(), clazz);
}
private static <T> T getAccessTokenFrom(String accessToken, Class<T> clazz) throws Exception {
return mapper.readValue(getDecodedPayload(accessToken), clazz);
}
public static String getRefreshTokenPayloadStringFrom(TokenResponse tokenResponse) throws Exception {
return getRefreshTokenPayloadStringFrom(tokenResponse, true);
}
public static String getRefreshTokenPayloadStringFrom(TokenResponse tokenResponse, boolean prettyPrint) throws Exception {
public static String getRefreshTokenPayloadStringFrom(TokenResponse tokenResponse, boolean prettyPrint)
throws Exception {
return toJSONString(getRefreshTokenFrom(tokenResponse, Object.class), prettyPrint);
}
@ -82,16 +95,50 @@ public class ModelUtils {
}
}
public static byte[] getDecodedHeader(String value) {
return getBase64Decoded(getEncodedHeader(value));
}
public static String getEncodedHeader(String encodedJWT) {
return splitAndGet(encodedJWT, 0);
}
public static byte[] getDecodedPayload(String value) {
return getBase64Decoded(getEncodedPayload(value));
}
public static String getEncodedPayload(String encodedJWT) {
return splitAndGet(encodedJWT, 1);
}
public static byte[] getDecodedSignature(String value) {
return getBase64Decoded(getEncodedSignature(value));
}
public static String getEncodedSignature(String encodedJWT) {
return splitAndGet(encodedJWT, 2);
}
public static String getClientIdFromToken(AccessToken accessToken) {
String clientId;
logger.debug("Client id not provided, using authorized party field (azp)");
clientId = accessToken.getIssuedFor();
if (clientId == null) {
logger.debug("Issued for field (azp) not present, getting first of the audience field (aud)");
clientId = getFirstAudienceNoAccount(accessToken);
}
return clientId;
}
private static String getFirstAudienceNoAccount(AccessToken accessToken) {
// Trying to get it from the token's audience ('aud' field), getting the first except the 'account'
List<String> tokenAud = Arrays.asList(accessToken.getAudience());
tokenAud.remove(ACCOUNT_AUDIENCE_RESOURCE);
if (tokenAud.size() > 0) {
return tokenAud.iterator().next();
} else {
// Setting it to empty string to avoid NPE in encoding
return "";
}
}
}

View File

@ -0,0 +1,14 @@
package org.gcube.common.keycloak.model;
public class OIDCConstants {
public static final String PERMISSION_PARAMETER = "permission";
public static final String GRANT_TYPE_PARAMETER = "grant_type";
public static final String UMA_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
public static final String AUDIENCE_PARAMETER = "audience";
public static final String REFRESH_TOKEN_GRANT_TYPE = "refresh_token";
public static final String REFRESH_TOKEN_PARAMETER = "refresh_token";
public static final String CLIENT_ID_PARAMETER = "client_id";
public static final String CLIENT_SECRET_PARAMETER = "client_secret";
}

View File

@ -8,10 +8,13 @@ import org.gcube.common.scope.api.ScopeProvider;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestKeycloakClient {
protected static final Logger logger = LoggerFactory.getLogger(TestKeycloakClient.class);
@ -21,6 +24,8 @@ public class TestKeycloakClient {
private static final String CLIENT_SECRET = "38f76152-2b7c-418f-9b67-66f4cc2f401e";
private static final String TEST_AUDIENCE = "conductor-server";
private static TokenResponse tr = null;
@Before
public void setUp() throws Exception {
ScopeProvider.instance.set("/gcube");
@ -31,7 +36,7 @@ public class TestKeycloakClient {
}
@Test
public void testEndpointDiscovery() throws Exception {
public void test1EndpointDiscovery() throws Exception {
logger.info("Start testing Keycloak endpoint discovery...");
URL url = KeycloakClientFactory.newInstance().findTokenEndpointURL();
Assert.assertNotNull(url);
@ -40,31 +45,38 @@ public class TestKeycloakClient {
}
@Test
public void testQueryUMATokenWithDiscoveryInCurrentScope() throws Exception {
public void test2QueryUMATokenWithDiscoveryInCurrentScope() throws Exception {
logger.info("Start testing query UMA token from Keycloak with endpoint discovery and current scope...");
TokenResponse tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, null);
tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void testQueryUMATokenWithDiscovery() throws Exception {
public void test3QueryUMATokenWithDiscovery() throws Exception {
logger.info("Start testing query UMA token from Keycloak with endpoint discovery...");
TokenResponse tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE,
null);
tr = KeycloakClientFactory.newInstance().queryUMAToken(CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void test4QueryUMAToken() throws Exception {
logger.info("Start testing query UMA token from Keycloak with URL...");
tr = KeycloakClientFactory.newInstance().queryUMAToken(new URL(DEV_ENDPOINT), CLIENT_ID, CLIENT_SECRET,
TEST_AUDIENCE, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
}
@Test
public void testQueryUMAToken() throws Exception {
logger.info("Start testing query UMA token from Keycloak with URL...");
TokenResponse tr = KeycloakClientFactory.newInstance()
.queryUMAToken(new URL(DEV_ENDPOINT), CLIENT_ID, CLIENT_SECRET, TEST_AUDIENCE, null);
TestModels.checkTokenResponse(tr);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(tr), "service-account-" + CLIENT_ID);
public void test5RefreshTokenWithDiscovery() throws Exception {
logger.info("Start testing refresh UMA token from Keycloak with endpoint discovery...");
TokenResponse refreshedTR = KeycloakClientFactory.newInstance().refreshToken(CLIENT_ID, CLIENT_SECRET, tr);
TestModels.checkTokenResponse(refreshedTR);
TestModels.checkAccessToken(ModelUtils.getAccessTokenFrom(refreshedTR), "service-account-" + CLIENT_ID);
TestModels.checkRefreshToken(ModelUtils.getRefreshTokenFrom(refreshedTR));
}
}

View File

@ -3,17 +3,20 @@ package org.gcube.common.keycloak;
import java.io.File;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.keycloak.model.AccessToken;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.keycloak.model.RefreshToken;
import org.gcube.common.keycloak.model.TokenResponse;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestModels {
protected static final Logger logger = LoggerFactory.getLogger(TestModels.class);
@ -56,6 +59,22 @@ public class TestModels {
checkAccessToken(at, null);
}
@Test
public void testRemoveBearerPrefixInHeader() throws Exception {
TokenResponse tr = new ObjectMapper().readValue(new File("src/test/resources/oidc-token-response.json"),
TokenResponse.class);
AccessToken at1 = ModelUtils.getAccessTokenFrom(tr.getAccessToken());
AccessToken at2 = ModelUtils.getAccessTokenFrom("Bearer " + tr.getAccessToken());
AccessToken at3 = ModelUtils.getAccessTokenFrom("bearer " + tr.getAccessToken());
checkAccessToken(at1, null);
checkAccessToken(at2, null);
checkAccessToken(at3, null);
Assert.assertEquals(ModelUtils.toJSONString(at1), ModelUtils.toJSONString(at2));
Assert.assertEquals(ModelUtils.toJSONString(at2), ModelUtils.toJSONString(at3));
}
@Test
public void testUMARefreshToken() throws Exception {
logger.info("Start testing refresh token object binding...");
@ -86,4 +105,5 @@ public class TestModels {
Assert.assertNotNull(rt.getOtherClaims());
Assert.assertNotNull(rt.getAudience());
}
}