Create session for the requester client in Token Exchange (#31290)
Closes #31180 Signed-off-by: rmartinc <rmartinc@redhat.com> Co-authored-by: Marek Posolda <mposolda@gmail.com>
This commit is contained in:
parent
593afbb4e0
commit
611e6d102e
2 changed files with 54 additions and 5 deletions
|
@ -396,15 +396,19 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType,
|
||||
ClientModel targetClient, String audience, String scope) {
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(targetClient);
|
||||
|
||||
private AuthenticationSessionModel createSessionModel(UserSessionModel targetUserSession, RootAuthenticationSessionModel rootAuthSession, UserModel targetUser, ClientModel client, String scope) {
|
||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
authSession.setAuthenticatedUser(targetUser);
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
||||
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
|
||||
return authSession;
|
||||
}
|
||||
|
||||
protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType,
|
||||
ClientModel targetClient, String audience, String scope) {
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
AuthenticationSessionModel authSession = createSessionModel(targetUserSession, rootAuthSession, targetUser, targetClient, scope);
|
||||
|
||||
if (targetUserSession == null) {
|
||||
// if no session is associated with a subject_token, a transient session is created to only allow building a token to the audience
|
||||
|
@ -417,6 +421,12 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
|||
AuthenticationManager.setClientScopesInSession(session, authSession);
|
||||
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
|
||||
|
||||
if (!AuthenticationManager.isClientSessionValid(realm, client, targetUserSession, targetUserSession.getAuthenticatedClientSessionByClient(client.getId()))) {
|
||||
// create the requester client session if needed
|
||||
AuthenticationSessionModel clientAuthSession = createSessionModel(targetUserSession, rootAuthSession, targetUser, client, scope);
|
||||
TokenManager.attachAuthenticationSession(this.session, targetUserSession, clientAuthSession);
|
||||
}
|
||||
|
||||
updateUserSessionFromClientAuth(targetUserSession);
|
||||
|
||||
TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSessionCtx)
|
||||
|
|
|
@ -1087,6 +1087,45 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExchangeForDifferentClient() throws Exception {
|
||||
testingClient.server().run(ClientTokenExchangeTest::setupRealm);
|
||||
|
||||
// generate the first token for a public client
|
||||
oauth.realm(TEST);
|
||||
oauth.clientId("direct-public");
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "user", "password");
|
||||
String accessToken = response.getAccessToken();
|
||||
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||
assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||
Assert.assertNotNull(token.getSessionId());
|
||||
String sid = token.getSessionId();
|
||||
|
||||
// perform token exchange with client-exchanger simulating it received the previous token
|
||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||
accessToken = response.getAccessToken();
|
||||
accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||
token = accessTokenVerifier.parse().getToken();
|
||||
Assert.assertEquals("client-exchanger", token.getIssuedFor());
|
||||
Assert.assertEquals("target", token.getAudience()[0]);
|
||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||
Assert.assertEquals(sid, token.getSessionId());
|
||||
|
||||
// perform a second token exchange just to check everything is OK
|
||||
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||
accessToken = response.getAccessToken();
|
||||
accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||
token = accessTokenVerifier.parse().getToken();
|
||||
Assert.assertEquals("client-exchanger", token.getIssuedFor());
|
||||
Assert.assertEquals("target", token.getAudience()[0]);
|
||||
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||
Assert.assertEquals(sid, token.getSessionId());
|
||||
}
|
||||
|
||||
private static void addDirectExchanger(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
RoleModel exampleRole = realm.addRole("example");
|
||||
|
|
Loading…
Reference in a new issue