Allow introspecting tokens issued during token exchange with delegation semantics
Closes #9337
This commit is contained in:
parent
ea4b4b97b4
commit
25be07be17
5 changed files with 114 additions and 19 deletions
|
@ -5,7 +5,8 @@ package org.keycloak.models;
|
||||||
*/
|
*/
|
||||||
public enum ImpersonationSessionNote implements UserSessionNoteDescriptor {
|
public enum ImpersonationSessionNote implements UserSessionNoteDescriptor {
|
||||||
IMPERSONATOR_ID("Impersonator User ID"),
|
IMPERSONATOR_ID("Impersonator User ID"),
|
||||||
IMPERSONATOR_USERNAME("Impersonator Username");
|
IMPERSONATOR_USERNAME("Impersonator Username"),
|
||||||
|
IMPERSONATOR_CLIENT("Impersonator Client");
|
||||||
|
|
||||||
final String displayName;
|
final String displayName;
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,11 @@ import org.keycloak.TokenVerifier;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.crypto.SignatureProvider;
|
import org.keycloak.crypto.SignatureProvider;
|
||||||
import org.keycloak.crypto.SignatureVerifierContext;
|
import org.keycloak.crypto.SignatureVerifierContext;
|
||||||
|
import org.keycloak.models.ImpersonationSessionNote;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -68,6 +70,21 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String sessionState = accessToken.getSessionState();
|
||||||
|
|
||||||
|
if (sessionState != null) {
|
||||||
|
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionState);
|
||||||
|
|
||||||
|
if (userSession != null) {
|
||||||
|
String actor = userSession.getNote(ImpersonationSessionNote.IMPERSONATOR_USERNAME.toString());
|
||||||
|
|
||||||
|
if (actor != null) {
|
||||||
|
// for token exchange delegation semantics when an entity (actor) other than the subject is the acting party to whom authority has been delegated
|
||||||
|
tokenMetadata.putObject("act").put("sub", actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tokenMetadata = JsonSerialization.createObjectNode();
|
tokenMetadata = JsonSerialization.createObjectNode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
|
||||||
import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError;
|
import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError;
|
||||||
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_CLIENT;
|
||||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
||||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
||||||
|
|
||||||
|
@ -339,7 +340,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
||||||
case OAuth2Constants.REFRESH_TOKEN_TYPE:
|
case OAuth2Constants.REFRESH_TOKEN_TYPE:
|
||||||
return exchangeClientToOIDCClient(targetUser, targetUserSession, requestedTokenType, targetClient, audience, scope);
|
return exchangeClientToOIDCClient(targetUser, targetUserSession, requestedTokenType, targetClient, audience, scope);
|
||||||
case OAuth2Constants.SAML2_TOKEN_TYPE:
|
case OAuth2Constants.SAML2_TOKEN_TYPE:
|
||||||
return exchangeClientToSAML2Client(targetUser, targetUserSession, requestedTokenType, targetClient, audience, scope);
|
return exchangeClientToSAML2Client(targetUser, targetUserSession, requestedTokenType, targetClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "requested_token_type unsupported", Response.Status.BAD_REQUEST);
|
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "requested_token_type unsupported", Response.Status.BAD_REQUEST);
|
||||||
|
@ -393,6 +394,11 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
||||||
responseBuilder.getAccessToken().addAudience(audience);
|
responseBuilder.getAccessToken().addAudience(audience);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (formParams.containsKey(OAuth2Constants.REQUESTED_SUBJECT)) {
|
||||||
|
// if "impersonation", store the client that originated the impersonated user session
|
||||||
|
targetUserSession.setNote(IMPERSONATOR_CLIENT.toString(), client.getId());
|
||||||
|
}
|
||||||
|
|
||||||
if (requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)
|
if (requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)
|
||||||
&& OIDCAdvancedConfigWrapper.fromClientModel(client).isUseRefreshToken()) {
|
&& OIDCAdvancedConfigWrapper.fromClientModel(client).isUseRefreshToken()) {
|
||||||
responseBuilder.generateRefreshToken();
|
responseBuilder.generateRefreshToken();
|
||||||
|
@ -412,8 +418,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
||||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response exchangeClientToSAML2Client(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType,
|
protected Response exchangeClientToSAML2Client(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType, ClientModel targetClient) {
|
||||||
ClientModel targetClient, String audience, String scope) {
|
|
||||||
// Create authSession with target SAML 2.0 client and authenticated user
|
// Create authSession with target SAML 2.0 client and authenticated user
|
||||||
LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory()
|
LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory()
|
||||||
.getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL);
|
.getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL);
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.ClientSessionContext;
|
import org.keycloak.models.ClientSessionContext;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.ImpersonationSessionNote;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
@ -259,6 +260,13 @@ public class TokenManager {
|
||||||
|
|
||||||
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
||||||
|
|
||||||
|
if (userSession == null) {
|
||||||
|
// also try to resolve sessions created during token exchange when the user is impersonated
|
||||||
|
userSession = session.sessions().getUserSessionWithPredicate(realm,
|
||||||
|
token.getSessionState(), false,
|
||||||
|
model -> client.getId().equals(model.getNote(ImpersonationSessionNote.IMPERSONATOR_CLIENT.toString())));
|
||||||
|
}
|
||||||
|
|
||||||
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||||
valid = isUserValid(session, realm, token, userSession.getUser());
|
valid = isUserValid(session, realm, token, userSession.getUser());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -69,12 +69,17 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
||||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -279,7 +284,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
{
|
{
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||||
|
@ -289,7 +294,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
||||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -301,7 +306,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("legal", exchangedToken.getIssuedFor());
|
Assert.assertEquals("legal", exchangedToken.getIssuedFor());
|
||||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "illegal", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "illegal", "secret");
|
||||||
|
@ -344,7 +349,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||||
String exchangedTokenString = response.getAccessToken();
|
String exchangedTokenString = response.getAccessToken();
|
||||||
|
@ -353,7 +358,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
||||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
// can exchange to itself because the client is within the audience of the token issued to the public client
|
// can exchange to itself because the client is within the audience of the token issued to the public client
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, null, "client-exchanger", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, null, "client-exchanger", "secret");
|
||||||
|
@ -388,7 +393,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
// client-exchanger can impersonate from token "user" to user "impersonated-user"
|
// client-exchanger can impersonate from token "user" to user "impersonated-user"
|
||||||
{
|
{
|
||||||
|
@ -445,7 +450,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
||||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "impersonated-user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "impersonated-user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Response response = exchangeUrl.request()
|
try (Response response = exchangeUrl.request()
|
||||||
|
@ -496,6 +501,65 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@UncaughtServerErrorExpected
|
||||||
|
public void testIntrospectTokenAfterImpersonation() throws Exception {
|
||||||
|
testingClient.server().run(ClientTokenExchangeTest::setupRealm);
|
||||||
|
|
||||||
|
oauth.realm(TEST);
|
||||||
|
oauth.clientId("client-exchanger");
|
||||||
|
|
||||||
|
Client httpClient = AdminClientUtil.createResteasyClient();
|
||||||
|
|
||||||
|
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
||||||
|
.path("/realms")
|
||||||
|
.path(TEST)
|
||||||
|
.path("protocol/openid-connect/token");
|
||||||
|
System.out.println("Exchange url: " + exchangeUrl.getUri().toString());
|
||||||
|
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret", "user", "password");
|
||||||
|
String accessToken = tokenResponse.getAccessToken();
|
||||||
|
|
||||||
|
try (Response response = exchangeUrl.request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret"))
|
||||||
|
.post(Entity.form(
|
||||||
|
new Form()
|
||||||
|
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
|
||||||
|
.param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user")
|
||||||
|
|
||||||
|
))) {
|
||||||
|
org.junit.Assert.assertEquals(200, response.getStatus());
|
||||||
|
AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||||
|
String exchangedTokenString = accessTokenResponse.getToken();
|
||||||
|
JsonNode json = JsonSerialization.readValue(oauth.introspectAccessTokenWithClientCredential("client-exchanger", "secret", exchangedTokenString), com.fasterxml.jackson.databind.JsonNode.class);
|
||||||
|
assertTrue(json.get("active").asBoolean());
|
||||||
|
assertEquals("impersonated-user", json.get("preferred_username").asText());
|
||||||
|
assertEquals("user", json.get("act").get("sub").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Response response = exchangeUrl.request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret"))
|
||||||
|
.post(Entity.form(
|
||||||
|
new Form()
|
||||||
|
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
|
||||||
|
.param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user")
|
||||||
|
.param(OAuth2Constants.AUDIENCE, "target")
|
||||||
|
|
||||||
|
))) {
|
||||||
|
org.junit.Assert.assertEquals(200, response.getStatus());
|
||||||
|
AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||||
|
String exchangedTokenString = accessTokenResponse.getToken();
|
||||||
|
JsonNode json = JsonSerialization.readValue(oauth.introspectAccessTokenWithClientCredential("client-exchanger", "secret", exchangedTokenString), com.fasterxml.jackson.databind.JsonNode.class);
|
||||||
|
assertTrue(json.get("active").asBoolean());
|
||||||
|
assertEquals("impersonated-user", json.get("preferred_username").asText());
|
||||||
|
assertEquals("user", json.get("act").get("sub").asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@UncaughtServerErrorExpected
|
@UncaughtServerErrorExpected
|
||||||
@Test
|
@Test
|
||||||
public void testImpersonationUsingPublicClient() throws Exception {
|
public void testImpersonationUsingPublicClient() throws Exception {
|
||||||
|
@ -512,7 +576,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
||||||
.path("/realms")
|
.path("/realms")
|
||||||
|
@ -561,7 +625,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
||||||
.path("/realms")
|
.path("/realms")
|
||||||
|
@ -621,7 +685,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "bad-impersonator");
|
Assert.assertEquals(token.getPreferredUsername(), "bad-impersonator");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
// test that user does not have impersonator permission
|
// test that user does not have impersonator permission
|
||||||
{
|
{
|
||||||
|
@ -699,7 +763,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("direct-legal", exchangedToken.getIssuedFor());
|
Assert.assertEquals("direct-legal", exchangedToken.getIssuedFor());
|
||||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "impersonated-user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "impersonated-user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// direct-public fails impersonation
|
// direct-public fails impersonation
|
||||||
|
@ -728,7 +792,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
.param(OAuth2Constants.AUDIENCE, "target")
|
.param(OAuth2Constants.AUDIENCE, "target")
|
||||||
|
|
||||||
));
|
));
|
||||||
Assert.assertTrue(response.getStatus() >= 400);
|
assertTrue(response.getStatus() >= 400);
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,7 +843,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, null, "client-exchanger", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, null, "client-exchanger", "secret");
|
||||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||||
|
@ -799,7 +863,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "direct-legal", "secret");
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "direct-legal", "secret");
|
||||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||||
|
@ -816,7 +880,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
// public client has no permission to exchange with the client direct-legal to which the token was issued for
|
// public client has no permission to exchange with the client direct-legal to which the token was issued for
|
||||||
// if not set, the audience is calculated based on the client to which the token was issued for
|
// if not set, the audience is calculated based on the client to which the token was issued for
|
||||||
|
|
Loading…
Reference in a new issue