Lightweight access tokens for Admin REST API (#32347)
* Lightweight access tokens for Admin REST API Closes #31513 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
d0a346066d
commit
a14548a7a2
9 changed files with 145 additions and 38 deletions
|
@ -168,3 +168,7 @@ For information on how to upgrade, see the link:{upgradingguide_link}[{upgrading
|
||||||
There are now generalized events for updating (`UPDATE_CREDENTIAL`) and removing (`REMOVE_CREDENTIAL`) a credential. The credential type is described in the `credential_type` attribute of the events. The new event types are supported by the Email Event Listener.
|
There are now generalized events for updating (`UPDATE_CREDENTIAL`) and removing (`REMOVE_CREDENTIAL`) a credential. The credential type is described in the `credential_type` attribute of the events. The new event types are supported by the Email Event Listener.
|
||||||
|
|
||||||
The following event types are now deprecated and will be removed in a future version: `UPDATE_PASSWORD`, `UPDATE_PASSWORD_ERROR`, `UPDATE_TOTP`, `UPDATE_TOTP_ERROR`, `REMOVE_TOTP`, `REMOVE_TOTP_ERROR`
|
The following event types are now deprecated and will be removed in a future version: `UPDATE_PASSWORD`, `UPDATE_PASSWORD_ERROR`, `UPDATE_TOTP`, `UPDATE_TOTP_ERROR`, `REMOVE_TOTP`, `REMOVE_TOTP_ERROR`
|
||||||
|
|
||||||
|
= Lightweight access tokens for Admin REST API
|
||||||
|
|
||||||
|
Lightweight access tokens can now be used on the admin REST API. The `security-admin-console` and `admin-cli` clients are now using lightweight access tokens by default, so “Always Use Lightweight Access Token” and “Full Scope Allowed” are now enabled on these two clients. However, the behavior in the admin console should effectively remain the same. Be cautious if you have made changes to these two clients and if you are using them for other purposes.
|
||||||
|
|
|
@ -21,7 +21,10 @@ package org.keycloak.migration.migrators;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.Config;
|
||||||
import org.keycloak.migration.ModelVersion;
|
import org.keycloak.migration.ModelVersion;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionProvider;
|
import org.keycloak.models.UserSessionProvider;
|
||||||
|
@ -55,6 +58,12 @@ public class MigrateTo26_0_0 implements Migration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void migrateRealm(KeycloakSession session, RealmModel realm) {
|
private void migrateRealm(KeycloakSession session, RealmModel realm) {
|
||||||
|
ClientModel adminConsoleClient = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
|
||||||
|
adminConsoleClient.setFullScopeAllowed(true);
|
||||||
|
adminConsoleClient.setAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED, String.valueOf(true));
|
||||||
|
ClientModel adminCliClient = realm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
adminCliClient.setFullScopeAllowed(true);
|
||||||
|
adminCliClient.setAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED, String.valueOf(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class KeycloakIdentity implements Identity {
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
throw new RuntimeException("No active session associated with the token");
|
throw new RuntimeException("No active session associated with the token");
|
||||||
}
|
}
|
||||||
|
@ -176,15 +176,22 @@ public class KeycloakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
|
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
|
||||||
|
this(accessToken, keycloakSession, keycloakSession.getContext().getRealm());
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, RealmModel realm) {
|
||||||
if (accessToken == null) {
|
if (accessToken == null) {
|
||||||
throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
|
throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
|
||||||
}
|
}
|
||||||
if (keycloakSession == null) {
|
if (keycloakSession == null) {
|
||||||
throw new ErrorResponseException("no_keycloak_session", "No keycloak session", Status.FORBIDDEN);
|
throw new ErrorResponseException("no_keycloak_session", "No keycloak session", Status.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
if (realm == null) {
|
||||||
|
throw new ErrorResponseException("no_keycloak_session", "No realm set", Status.FORBIDDEN);
|
||||||
|
}
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.keycloakSession = keycloakSession;
|
this.keycloakSession = keycloakSession;
|
||||||
this.realm = keycloakSession.getContext().getRealm();
|
this.realm = realm;
|
||||||
|
|
||||||
Map<String, Collection<String>> attributes = new HashMap<>();
|
Map<String, Collection<String>> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,19 @@ import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
import org.keycloak.models.ClientInitialAccessModel;
|
import org.keycloak.models.ClientInitialAccessModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientSessionContext;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
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;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.UserSessionProvider;
|
||||||
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.JsonWebToken;
|
import org.keycloak.representations.JsonWebToken;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||||
|
@ -43,11 +48,16 @@ import org.keycloak.services.clientpolicy.context.DynamicClientViewContext;
|
||||||
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyException;
|
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyException;
|
||||||
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager;
|
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager;
|
||||||
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
|
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
|
||||||
|
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
|
||||||
import jakarta.ws.rs.WebApplicationException;
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.keycloak.utils.RoleResolveUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -283,36 +293,43 @@ public class ClientRegistrationAuth {
|
||||||
|
|
||||||
private boolean hasRole(String... roles) {
|
private boolean hasRole(String... roles) {
|
||||||
try {
|
try {
|
||||||
if (jwt.getIssuedFor().equals(Constants.ADMIN_CLI_CLIENT_ID)
|
|
||||||
|| jwt.getIssuedFor().equals(Constants.ADMIN_CONSOLE_CLIENT_ID)) {
|
|
||||||
return hasRoleInModel(roles);
|
|
||||||
|
|
||||||
} else {
|
//support for lightweight access token
|
||||||
return hasRoleInToken(roles);
|
if (jwt.getSubject() == null) {
|
||||||
|
String sid = (String) jwt.getOtherClaims().get("sid");
|
||||||
|
if (sid != null) {
|
||||||
|
final String issuedFor = jwt.getIssuedFor();
|
||||||
|
UserSessionProvider sessions = session.sessions();
|
||||||
|
UserSessionModel userSession = sessions.getUserSession(realm, sid);
|
||||||
|
if (userSession == null) {
|
||||||
|
userSession = sessions.getOfflineUserSession(realm, sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSession != null) {
|
||||||
|
//get client session
|
||||||
|
ClientModel client = realm.getClientByClientId(issuedFor);
|
||||||
|
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
|
|
||||||
|
//set realm roles
|
||||||
|
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, (String) jwt.getOtherClaims().get("scope"), session);
|
||||||
|
Map<String, AccessToken.Access> resourceAccess = RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx);
|
||||||
|
|
||||||
|
Map<String, Map<String, List<String>>> resourceAccessMap = new HashMap<>();
|
||||||
|
resourceAccess.forEach((key, access) ->
|
||||||
|
resourceAccessMap.put(key, Map.of("roles", new ArrayList<>(access.getRoles())))
|
||||||
|
);
|
||||||
|
jwt.setSubject(userSession.getUser().getId());
|
||||||
|
jwt.getOtherClaims().put("resource_access", resourceAccessMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return hasRoleInToken(roles);
|
||||||
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasRoleInModel(String[] roles) {
|
|
||||||
ClientModel roleNamespace;
|
|
||||||
UserModel user = session.users().getUserById(realm, jwt.getSubject());
|
|
||||||
if (user == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (realm.getName().equals(Config.getAdminRealm())) {
|
|
||||||
roleNamespace = realm.getMasterAdminClient();
|
|
||||||
} else {
|
|
||||||
roleNamespace = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
|
||||||
}
|
|
||||||
for (String role : roles) {
|
|
||||||
RoleModel roleModel = roleNamespace.getRole(role);
|
|
||||||
if (user.hasRole(roleModel)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasRoleInToken(String[] role) {
|
private boolean hasRoleInToken(String[] role) {
|
||||||
Map<String, Object> otherClaims = jwt.getOtherClaims();
|
Map<String, Object> otherClaims = jwt.getOtherClaims();
|
||||||
if (otherClaims != null) {
|
if (otherClaims != null) {
|
||||||
|
|
|
@ -188,11 +188,12 @@ public class RealmManager {
|
||||||
|
|
||||||
adminConsole.setEnabled(true);
|
adminConsole.setEnabled(true);
|
||||||
adminConsole.setAlwaysDisplayInConsole(false);
|
adminConsole.setAlwaysDisplayInConsole(false);
|
||||||
|
adminConsole.setFullScopeAllowed(true);
|
||||||
adminConsole.setPublicClient(true);
|
adminConsole.setPublicClient(true);
|
||||||
adminConsole.setFullScopeAllowed(false);
|
|
||||||
adminConsole.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
adminConsole.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
|
||||||
adminConsole.setAttribute(OIDCConfigAttributes.PKCE_CODE_CHALLENGE_METHOD, "S256");
|
adminConsole.setAttribute(OIDCConfigAttributes.PKCE_CODE_CHALLENGE_METHOD, "S256");
|
||||||
|
adminConsole.setAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED, "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupAdminConsoleLocaleMapper(RealmModel realm) {
|
protected void setupAdminConsoleLocaleMapper(RealmModel realm) {
|
||||||
|
@ -214,10 +215,11 @@ public class RealmManager {
|
||||||
adminCli.setName("${client_" + Constants.ADMIN_CLI_CLIENT_ID + "}");
|
adminCli.setName("${client_" + Constants.ADMIN_CLI_CLIENT_ID + "}");
|
||||||
adminCli.setEnabled(true);
|
adminCli.setEnabled(true);
|
||||||
adminCli.setAlwaysDisplayInConsole(false);
|
adminCli.setAlwaysDisplayInConsole(false);
|
||||||
adminCli.setFullScopeAllowed(false);
|
adminCli.setFullScopeAllowed(true);
|
||||||
adminCli.setStandardFlowEnabled(false);
|
adminCli.setStandardFlowEnabled(false);
|
||||||
adminCli.setDirectAccessGrantsEnabled(true);
|
adminCli.setDirectAccessGrantsEnabled(true);
|
||||||
adminCli.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
adminCli.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
adminCli.setAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED, "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -644,7 +646,7 @@ public class RealmManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String determineDefaultRoleName(RealmRepresentation rep) {
|
private String determineDefaultRoleName(RealmRepresentation rep) {
|
||||||
String defaultRoleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + rep.getRealm().toLowerCase();
|
String defaultRoleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + rep.getRealm().toLowerCase();
|
||||||
if (! hasRealmRole(rep, defaultRoleName)) {
|
if (! hasRealmRole(rep, defaultRoleName)) {
|
||||||
return defaultRoleName;
|
return defaultRoleName;
|
||||||
} else {
|
} else {
|
||||||
|
@ -778,7 +780,7 @@ public class RealmManager {
|
||||||
ClientModel clientModel = Optional.ofNullable(client.getId())
|
ClientModel clientModel = Optional.ofNullable(client.getId())
|
||||||
.map(realmModel::getClientById)
|
.map(realmModel::getClientById)
|
||||||
.orElseGet(() -> realmModel.getClientByClientId(client.getClientId()));
|
.orElseGet(() -> realmModel.getClientByClientId(client.getClientId()));
|
||||||
|
|
||||||
if (clientModel == null) {
|
if (clientModel == null) {
|
||||||
throw new RuntimeException("Cannot find provided client by dir import.");
|
throw new RuntimeException("Cannot find provided client by dir import.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,17 @@ import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientSessionContext;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
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.models.UserSessionProvider;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.authorization.Permission;
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.resources.admin.AdminAuth;
|
import org.keycloak.services.resources.admin.AdminAuth;
|
||||||
|
@ -43,8 +48,11 @@ import org.keycloak.services.resources.admin.AdminAuth;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import jakarta.ws.rs.ForbiddenException;
|
import jakarta.ws.rs.ForbiddenException;
|
||||||
|
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||||
|
import org.keycloak.utils.RoleResolveUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -98,17 +106,30 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
|
|
||||||
private void initIdentity(KeycloakSession session, AdminAuth auth) {
|
private void initIdentity(KeycloakSession session, AdminAuth auth) {
|
||||||
final String issuedFor = auth.getToken().getIssuedFor();
|
final String issuedFor = auth.getToken().getIssuedFor();
|
||||||
|
AccessToken accessToken = auth.getToken();
|
||||||
|
//support for lightweight access token
|
||||||
|
if (auth.getToken().getSubject() == null) {
|
||||||
|
//get user session
|
||||||
|
UserSessionProvider sessions = session.sessions();
|
||||||
|
UserSessionModel userSession = sessions.getUserSession(adminsRealm, auth.getToken().getSessionId());
|
||||||
|
if (userSession == null) {
|
||||||
|
userSession = sessions.getOfflineUserSession(adminsRealm, auth.getToken().getSessionId());
|
||||||
|
}
|
||||||
|
|
||||||
if (Constants.ADMIN_CLI_CLIENT_ID.equals(issuedFor) || Constants.ADMIN_CONSOLE_CLIENT_ID.equals(issuedFor)) {
|
if (userSession != null) {
|
||||||
this.identity = new UserModelIdentity(auth.getRealm(), auth.getUser());
|
//get client session
|
||||||
} else {
|
ClientModel client = adminsRealm.getClientByClientId(issuedFor);
|
||||||
ClientModel client = session.clients().getClientByClientId(auth.getRealm(), issuedFor);
|
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
if (client != null && Boolean.parseBoolean(client.getAttribute(Constants.SECURITY_ADMIN_CONSOLE_ATTR))) {
|
|
||||||
this.identity = new UserModelIdentity(auth.getRealm(), auth.getUser());
|
//set realm roles
|
||||||
} else {
|
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, auth.getToken().getScope(), session);
|
||||||
this.identity = new KeycloakIdentity(auth.getToken(), session);
|
AccessToken.Access realmAccess = RoleResolveUtil.getResolvedRealmRoles(session, clientSessionCtx, false);
|
||||||
|
Map<String, AccessToken.Access> clientAccess = RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx);
|
||||||
|
accessToken.setRealmAccess(realmAccess);
|
||||||
|
accessToken.setResourceAccess(clientAccess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.identity = new KeycloakIdentity(accessToken, session, adminsRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
MgmtPermissions(KeycloakSession session, RealmModel adminsRealm, UserModel admin) {
|
MgmtPermissions(KeycloakSession session, RealmModel adminsRealm, UserModel admin) {
|
||||||
|
|
|
@ -434,6 +434,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
if (testIdentityProviderConfigMigration) {
|
if (testIdentityProviderConfigMigration) {
|
||||||
testIdentityProviderConfigMigration(migrationRealm2);
|
testIdentityProviderConfigMigration(migrationRealm2);
|
||||||
}
|
}
|
||||||
|
testLightweightClientAndFullScopeAllowed(masterRealm, Constants.ADMIN_CONSOLE_CLIENT_ID);
|
||||||
|
testLightweightClientAndFullScopeAllowed(masterRealm, Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
testLightweightClientAndFullScopeAllowed(migrationRealm, Constants.ADMIN_CONSOLE_CLIENT_ID);
|
||||||
|
testLightweightClientAndFullScopeAllowed(migrationRealm, Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testClientContainsExpectedClientScopes() {
|
private void testClientContainsExpectedClientScopes() {
|
||||||
|
@ -1351,4 +1355,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
assertThat(rep.isHideOnLogin(), is(true));
|
assertThat(rep.isHideOnLogin(), is(true));
|
||||||
assertThat(rep.getConfig().containsKey(IdentityProviderModel.LEGACY_HIDE_ON_LOGIN_ATTR), is(false));
|
assertThat(rep.getConfig().containsKey(IdentityProviderModel.LEGACY_HIDE_ON_LOGIN_ATTR), is(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void testLightweightClientAndFullScopeAllowed(RealmResource realm, String clientId) {
|
||||||
|
ClientRepresentation clientRepresentation = realm.clients().findByClientId(clientId).get(0);
|
||||||
|
assertTrue(clientRepresentation.isFullScopeAllowed());
|
||||||
|
assertTrue(Boolean.parseBoolean(clientRepresentation.getAttributes().get(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,15 @@ package org.keycloak.testsuite.oidc;
|
||||||
|
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.admin.client.resource.ClientScopeResource;
|
import org.keycloak.admin.client.resource.ClientScopeResource;
|
||||||
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
@ -486,6 +491,36 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminConsoleClientWithLightweightAccessToken() {
|
||||||
|
|
||||||
|
oauth.realm("master");
|
||||||
|
oauth.clientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
|
||||||
|
oauth.redirectUri(OAuthClient.SERVER_ROOT + "/auth/admin/master/console");
|
||||||
|
PkceGenerator pkce = new PkceGenerator();
|
||||||
|
oauth.codeChallenge(pkce.getCodeChallenge());
|
||||||
|
oauth.codeChallengeMethod(OAuth2Constants.PKCE_METHOD_S256);
|
||||||
|
oauth.codeVerifier(pkce.getCodeVerifier());
|
||||||
|
|
||||||
|
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin("admin", "admin");
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||||
|
String accessToken = tokenResponse.getAccessToken();
|
||||||
|
logger.debug("access token:" + accessToken);
|
||||||
|
assertBasicClaims(oauth.verifyToken(accessToken), true, true);
|
||||||
|
|
||||||
|
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
|
||||||
|
HttpGet get = new HttpGet(OAuthClient.SERVER_ROOT + "/auth/admin/realms/master");
|
||||||
|
get.setHeader("Authorization", "Bearer " + accessToken);
|
||||||
|
try (CloseableHttpResponse response = client.execute(get)) {
|
||||||
|
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
|
||||||
|
RealmRepresentation realmRepresentation = JsonSerialization.readValue(response.getEntity().getContent(), RealmRepresentation.class);
|
||||||
|
Assert.assertEquals("master", realmRepresentation.getRealm());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Assert.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void removeSession(final String sessionId) {
|
private void removeSession(final String sessionId) {
|
||||||
testingClient.testing().removeExpired(REALM_NAME);
|
testingClient.testing().removeExpired(REALM_NAME);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -204,7 +204,9 @@ public class AssertAdminEvents implements TestRule {
|
||||||
|
|
||||||
AuthDetailsRepresentation actualAuth = actual.getAuthDetails();
|
AuthDetailsRepresentation actualAuth = actual.getAuthDetails();
|
||||||
Assert.assertEquals(expectedAuth.getRealmId(), actualAuth.getRealmId());
|
Assert.assertEquals(expectedAuth.getRealmId(), actualAuth.getRealmId());
|
||||||
Assert.assertEquals(expectedAuth.getUserId(), actualAuth.getUserId());
|
if(expectedAuth.getUserId() != null) {
|
||||||
|
Assert.assertEquals(expectedAuth.getUserId(), actualAuth.getUserId());
|
||||||
|
}
|
||||||
if (expectedAuth.getClientId() != null) {
|
if (expectedAuth.getClientId() != null) {
|
||||||
Assert.assertEquals(expectedAuth.getClientId(), actualAuth.getClientId());
|
Assert.assertEquals(expectedAuth.getClientId(), actualAuth.getClientId());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue