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,
|
private AuthenticationSessionModel createSessionModel(UserSessionModel targetUserSession, RootAuthenticationSessionModel rootAuthSession, UserModel targetUser, ClientModel client, String scope) {
|
||||||
ClientModel targetClient, String audience, String scope) {
|
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
||||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
|
||||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(targetClient);
|
|
||||||
|
|
||||||
authSession.setAuthenticatedUser(targetUser);
|
authSession.setAuthenticatedUser(targetUser);
|
||||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
||||||
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
|
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 (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
|
// 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);
|
AuthenticationManager.setClientScopesInSession(session, authSession);
|
||||||
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, 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);
|
updateUserSessionFromClientAuth(targetUserSession);
|
||||||
|
|
||||||
TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSessionCtx)
|
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) {
|
private static void addDirectExchanger(KeycloakSession session) {
|
||||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||||
RoleModel exampleRole = realm.addRole("example");
|
RoleModel exampleRole = realm.addRole("example");
|
||||||
|
|
Loading…
Reference in a new issue