diff --git a/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationMembershipMapper.java b/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationMembershipMapper.java index 5a62a34ab36f..0b427e5c9548 100644 --- a/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationMembershipMapper.java +++ b/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationMembershipMapper.java @@ -45,6 +45,7 @@ import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; +import org.keycloak.protocol.oidc.mappers.OrganizationAwareMapper; import org.keycloak.protocol.oidc.mappers.TokenIntrospectionTokenMapper; import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper; import org.keycloak.provider.EnvironmentDependentProviderFactory; @@ -54,7 +55,7 @@ import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.JSON_TYPE; import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME; -public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory { +public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory, OrganizationAwareMapper { public static final String PROVIDER_ID = "oidc-organization-membership-mapper"; public static final String ADD_ORGANIZATION_ATTRIBUTES = "addOrganizationAttributes"; diff --git a/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationScope.java b/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationScope.java index 5b73d7517dbf..460bab05666d 100644 --- a/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationScope.java +++ b/services/src/main/java/org/keycloak/organization/protocol/mappers/oidc/OrganizationScope.java @@ -39,9 +39,11 @@ import org.keycloak.models.OrganizationModel; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; +import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; import org.keycloak.protocol.oidc.TokenManager; +import org.keycloak.protocol.oidc.mappers.OrganizationAwareMapper; import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.utils.StringUtil; @@ -415,9 +417,11 @@ private static ClientScopeModel resolveClientScope(KeycloakSession session, Stri ClientScopeModel clientScope = getClientScope(client, scope); if (clientScope != null) { - Stream mappers = clientScope.getProtocolMappersStream().map(ProtocolMapperModel::getProtocolMapper); - - if (mappers.noneMatch(OrganizationMembershipMapper.PROVIDER_ID::equals)) { + if (clientScope.getProtocolMappersStream() + .map(ProtocolMapperModel::getProtocolMapper) + .map(id -> session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, id)) + .filter(Objects::nonNull) + .noneMatch(OrganizationAwareMapper.class::isInstance)) { Set nonOrganizationScopes = session.getAttributeOrDefault(UNSUPPORTED_ORGANIZATION_SCOPES_ATTRIBUTE, Set.of()); if (nonOrganizationScopes.isEmpty()) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OrganizationAwareMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OrganizationAwareMapper.java new file mode 100644 index 000000000000..44cee928929e --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OrganizationAwareMapper.java @@ -0,0 +1,16 @@ +package org.keycloak.protocol.oidc.mappers; + +/** + * Marker interface for protocol mappers that handle organization claims. + * + *

Mappers implementing this interface are recognized by + * {@link org.keycloak.organization.protocol.mappers.oidc.OrganizationScope OrganizationScope} + * as organization-aware, enabling scope-based organization resolution for the + * client scope containing the mapper. + * + *

This follows the same pattern as {@link OIDCAccessTokenMapper} and + * {@link UserInfoTokenMapper}, where mapper capabilities are declared + * through interface implementation and checked via {@code instanceof}. + */ +public interface OrganizationAwareMapper { +}