Persistent user session for client scopes evaluate function

Closes #37202

Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
Giuseppe Graziano 2025-05-08 11:01:23 +02:00 committed by Marek Posolda
parent 603ecf20eb
commit 5505f26cf8
4 changed files with 162 additions and 2 deletions

View File

@ -165,6 +165,9 @@ public interface ClientResource {
@Path("/roles")
RolesResource roles();
@Path("/evaluate-scopes")
ClientScopeEvaluateResource clientScopesEvaluate();
/**
* Get default client scopes. Only name and ids are returned.
*

View File

@ -0,0 +1,46 @@
/*
* Copyright 2025 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.admin.client.resource;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.Map;
/**
* @author <a href="mailto:ggrazian@redhat.com">Giuseppe Graziano</a>
*/
public interface ClientScopeEvaluateResource {
@GET
@Path("generate-example-access-token")
AccessToken generateAccessToken(@QueryParam("scope") String scopeParam, @QueryParam("userId") String userId, @QueryParam("audience") String audience);
@GET
@Path("generate-example-id-token")
IDToken generateExampleIdToken(@QueryParam("scope") String scopeParam, @QueryParam("userId") String userId, @QueryParam("audience") String audience);
@GET
@Path("generate-example-userinfo")
Map<String, Object> generateExampleUserinfo(@QueryParam("scope") String scopeParam, @QueryParam("userId") String userId);
}

View File

@ -274,6 +274,7 @@ public class ClientScopeEvaluateResource {
private<R> R sessionAware(UserModel user, String scopeParam, String audienceParam, TriFunction<UserSessionModel, ClientSessionContext, ClientModel[], R> function) {
AuthenticationSessionModel authSession = null;
UserSessionModel userSession = null;
AuthenticationSessionManager authSessionManager = new AuthenticationSessionManager(session);
try {
@ -285,8 +286,8 @@ public class ClientScopeEvaluateResource {
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scopeParam);
UserSessionModel userSession = new UserSessionManager(session).createUserSession(authSession.getParentSession().getId(), realm, user, user.getUsername(),
clientConnection.getRemoteHost(), "example-auth", false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
userSession = new UserSessionManager(session).createUserSession(authSession.getParentSession().getId(), realm, user, user.getUsername(),
clientConnection.getRemoteHost(), "example-auth", false, null, null, UserSessionModel.SessionPersistenceState.PERSISTENT);
AuthenticationManager.setClientScopesInSession(session, authSession);
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
@ -302,6 +303,9 @@ public class ClientScopeEvaluateResource {
if (authSession != null) {
authSessionManager.removeAuthenticationSession(realm, authSession, false);
}
if (userSession != null) {
session.sessions().removeUserSession(realm, userSession);
}
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2025 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.testsuite.admin.client;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author <a href="mailto:ggrazian@redhat.com">Giuseppe Graziano</a>
*/
public class ClientScopeEvaluateTest extends AbstractClientTest {
private ClientResource accountClient;
@Before
public void init() {
accountClient = findClientResourceById("account");
createTestUserWithAdminClient();
getCleanup().addUserId(testUser.getId());
}
@Test
public void testGenerateAccessToken() {
AccessToken accessToken = accountClient.clientScopesEvaluate().generateAccessToken("openid", testUser.getId(), null);
assertNotNull(accessToken);
assertNotNull(accessToken.getSubject());
assertNotNull(accessToken.getPreferredUsername());
List<UserSessionRepresentation> sessions = accountClient.getUserSessions(0, 5);
assertEquals(0, sessions.size());
}
@Test
public void testGenerateIdToken() {
IDToken idToken = accountClient.clientScopesEvaluate().generateExampleIdToken("openid", testUser.getId(), null);
assertNotNull(idToken);
assertNotNull(idToken.getSubject());
assertNotNull(idToken.getPreferredUsername());
}
@Test
public void testGenerateUserInfo() {
Map<String, Object> userinfo = accountClient.clientScopesEvaluate().generateExampleUserinfo("openid", testUser.getId());
assertFalse(userinfo.isEmpty());
assertNotNull(userinfo.get(IDToken.SUBJECT));
assertNotNull(userinfo.get(IDToken.PREFERRED_USERNAME));
}
@Test
public void testGenerateAccessTokenWithoutBasicScope() {
String basicScopeId = ApiUtil.findClientScopeByName(testRealmResource(),"basic").toRepresentation().getId();
accountClient.removeDefaultClientScope(basicScopeId);
AccessToken accessToken = accountClient.clientScopesEvaluate().generateAccessToken("openid", testUser.getId(), null);
assertNotNull(accessToken);
assertNull(accessToken.getSubject());
accountClient.addDefaultClientScope(basicScopeId);
}
@Test
public void testGenerateAccessTokenWithOptionalScope() {
String emailScopeId = ApiUtil.findClientScopeByName(testRealmResource(),"email").toRepresentation().getId();
accountClient.removeDefaultClientScope(emailScopeId);
accountClient.addOptionalClientScope(emailScopeId);
AccessToken accessToken = accountClient.clientScopesEvaluate().generateAccessToken("openid", testUser.getId(), null);
assertNotNull(accessToken);
assertNull(accessToken.getEmail());
accessToken = accountClient.clientScopesEvaluate().generateAccessToken("openid email", testUser.getId(), null);
assertNotNull(accessToken);
assertNotNull(accessToken.getEmail());
accountClient.removeOptionalClientScope(emailScopeId);
accountClient.addDefaultClientScope(emailScopeId);
}
}