Service Provider Interface (SPI) contracts and abstractions for the Keycloak identity and access management server enabling extensibility through custom providers
—
Organization management provides multi-tenant capabilities within Keycloak, allowing for the creation and management of organizations with their own members, identity providers, and domain-based authentication routing. This SPI enables enterprise scenarios where multiple organizations share a single Keycloak realm while maintaining organizational boundaries.
The primary interface for managing organizations and their associated data within a realm.
/**
* A Provider that manages organization and its data within the scope of a realm.
*/
public interface OrganizationProvider extends Provider {
/**
* Creates a new organization with given name and alias to the realm.
* @param name the name of the organization
* @param alias the alias of the organization. If not set, defaults to name. Once set, the alias is immutable
* @throws ModelDuplicateException If there is already an organization with the given name or alias
* @return Model of the created organization
*/
OrganizationModel create(String name, String alias);
/**
* Creates a new organization with given id, name, and alias to the realm
* @param id the id of the organization
* @param name the name of the organization
* @param alias the alias of the organization
* @throws ModelDuplicateException If there is already an organization with the given name or alias
* @return Model of the created organization
*/
OrganizationModel create(String id, String name, String alias);
/**
* Returns an OrganizationModel by its id
* @param id the id of an organization
* @return the organization with the given id or null if there is no such organization
*/
OrganizationModel getById(String id);
/**
* Returns an OrganizationModel by its internet domain
* @param domainName the organization's internet domain (e.g. redhat.com)
* @return the organization that is linked to the given internet domain
*/
OrganizationModel getByDomainName(String domainName);
/**
* Returns an OrganizationModel with the given alias
* @param alias the alias
* @return the organization
*/
OrganizationModel getByAlias(String alias);
/**
* Returns all organizations in the realm
* @return a Stream of the realm's organizations
*/
Stream<OrganizationModel> getAllStream();
/**
* Returns all organizations in the realm filtered according to the specified parameters
* @param search a String representing either an organization name or domain
* @param exact if true, organizations will be searched using exact match
* @param first the position of the first result to be processed (pagination offset)
* @param max the maximum number of results to be returned
* @return a Stream of the matched organizations
*/
Stream<OrganizationModel> getAllStream(String search, Boolean exact, Integer first, Integer max);
/**
* Returns all organizations in the realm filtered according to the specified parameters
* @param attributes a Map containing the attributes (name/value) that must match organization attributes
* @param first the position of the first result to be processed (pagination offset)
* @param max the maximum number of results to be returned
* @return a Stream of the matched organizations
*/
Stream<OrganizationModel> getAllStream(Map<String, String> attributes, Integer first, Integer max);
/**
* Removes the given organization from the realm together with the data associated with it
* @param organization Organization to be removed
* @throws ModelException if the organization doesn't exist or doesn't belong to the realm
* @return true if the organization was removed, false otherwise
*/
boolean remove(OrganizationModel organization);
/**
* Removes all organizations from the realm
*/
void removeAll();
/**
* Returns number of organizations in the realm
* @return Number of organizations
*/
long count();
/**
* Indicates if the current realm supports organization
* @return true if organization is supported, false otherwise
*/
boolean isEnabled();
}Comprehensive member management with support for both managed and unmanaged members.
public interface OrganizationProvider extends Provider {
/**
* Adds the given UserModel as a managed member of the given OrganizationModel.
* @param organization the organization
* @param user the user
* @throws ModelException if the UserModel is member of different organization
* @return true if the user was added as a member, false otherwise
*/
boolean addManagedMember(OrganizationModel organization, UserModel user);
/**
* Adds the given UserModel as an unmanaged member of the given OrganizationModel.
* @param organization the organization
* @param user the user
* @throws ModelException if the UserModel is member of different organization
* @return true if the user was added as a member, false otherwise
*/
boolean addMember(OrganizationModel organization, UserModel user);
/**
* Returns the members of a given OrganizationModel filtered according to the specified filters.
* @param organization the organization
* @param filters Map containing filters for member search
* @param exact if true, exact match for filters
* @param first the position of the first result (pagination offset)
* @param max the maximum number of results to be returned
* @return Stream of the members
*/
Stream<UserModel> getMembersStream(OrganizationModel organization, Map<String, String> filters, Boolean exact, Integer first, Integer max);
/**
* Returns number of members in the organization
* @param organization the organization
* @return Number of members in the organization
*/
long getMembersCount(OrganizationModel organization);
/**
* Returns the member of the OrganizationModel by its id
* @param organization the organization
* @param id the member id
* @return the member of the OrganizationModel with the given id
*/
UserModel getMemberById(OrganizationModel organization, String id);
/**
* Returns the OrganizationModel that the member belongs to
* @param member the member of an organization
* @return the organizations the member belongs to or an empty stream if the user doesn't belong to any
*/
Stream<OrganizationModel> getByMember(UserModel member);
/**
* Indicates if the given member is managed by the organization.
* A member is managed by the organization whenever the member cannot exist without the organization
* @param organization the organization
* @param member the member
* @return true if the member is managed by the given organization
*/
boolean isManagedMember(OrganizationModel organization, UserModel member);
/**
* Indicates if the given user is a member of the given organization
* @param organization the organization
* @param user the member
* @return true if the user is a member, false otherwise
*/
boolean isMember(OrganizationModel organization, UserModel user);
/**
* Removes a member from the organization.
* This method can either remove the given member entirely from the realm or only remove the link to the organization.
* The decision depends on whether the user is managed by the organization or not.
* @param organization the organization
* @param member the member
* @return true if the given member is a member and was successfully removed from the organization
*/
boolean removeMember(OrganizationModel organization, UserModel member);
}Associate identity providers with organizations for automatic user routing and federated authentication.
public interface OrganizationProvider extends Provider {
/**
* Associate the given IdentityProviderModel with the given OrganizationModel
* @param organization the organization
* @param identityProvider the identityProvider
* @return true if the identityProvider was associated with the organization, false otherwise
*/
boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider);
/**
* Returns Stream of the identity providers associated with the given organization
* @param organization the organization
* @return Stream of the identity providers associated with the organization
*/
Stream<IdentityProviderModel> getIdentityProviders(OrganizationModel organization);
/**
* Removes the link between the given OrganizationModel and the identity provider if such a link exists
* @param organization the organization
* @param identityProvider the identity provider
* @return true if the link was removed, false otherwise
*/
boolean removeIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider);
}The core model representing an organization with its attributes, domains, and configuration.
/**
* Model representation of an organization within a realm.
*/
public interface OrganizationModel {
String ORGANIZATION_ATTRIBUTE = "kc.org";
String ORGANIZATION_NAME_ATTRIBUTE = "kc.org.name";
String ORGANIZATION_DOMAIN_ATTRIBUTE = "kc.org.domain";
String ALIAS = "alias";
/**
* Identity provider redirect modes for organization-based routing.
*/
enum IdentityProviderRedirectMode {
EMAIL_MATCH("kc.org.broker.redirect.mode.email-matches");
private final String key;
IdentityProviderRedirectMode(String key) {
this.key = key;
}
public boolean isSet(IdentityProviderModel broker) {
return Boolean.parseBoolean(broker.getConfig().get(key));
}
public String getKey() {
return key;
}
}
/**
* Returns the unique identifier of the organization
* @return the organization ID
*/
String getId();
/**
* Sets the name of the organization
* @param name the organization name
*/
void setName(String name);
/**
* Returns the name of the organization
* @return the organization name
*/
String getName();
/**
* Returns the alias of the organization
* @return the organization alias
*/
String getAlias();
/**
* Sets the alias of the organization
* @param alias the organization alias
*/
void setAlias(String alias);
/**
* Returns whether the organization is enabled
* @return true if enabled, false otherwise
*/
boolean isEnabled();
/**
* Sets whether the organization is enabled
* @param enabled true to enable, false to disable
*/
void setEnabled(boolean enabled);
/**
* Returns the description of the organization
* @return the organization description
*/
String getDescription();
/**
* Sets the description of the organization
* @param description the organization description
*/
void setDescription(String description);
/**
* Returns the redirect URL for the organization
* @return the redirect URL
*/
String getRedirectUrl();
/**
* Sets the redirect URL for the organization
* @param redirectUrl the redirect URL
*/
void setRedirectUrl(String redirectUrl);
/**
* Returns the organization's attributes
* @return Map of organization attributes
*/
Map<String, List<String>> getAttributes();
/**
* Sets the organization's attributes
* @param attributes Map of organization attributes
*/
void setAttributes(Map<String, List<String>> attributes);
/**
* Returns the domains associated with the organization
* @return Stream of organization domains
*/
Stream<OrganizationDomainModel> getDomains();
/**
* Sets the domains associated with the organization
* @param domains Set of organization domains
*/
void setDomains(Set<OrganizationDomainModel> domains);
/**
* Returns the identity providers associated with the organization
* @return Stream of identity providers
*/
Stream<IdentityProviderModel> getIdentityProviders();
/**
* Indicates if the given user is managed by the organization
* @param user the user
* @return true if the user is managed by the organization
*/
boolean isManaged(UserModel user);
/**
* Indicates if the given user is a member of the organization
* @param user the user
* @return true if the user is a member of the organization
*/
boolean isMember(UserModel user);
}Model representing internet domains associated with organizations for email-based routing and verification.
/**
* Model implementation of an organization internet domain.
*/
public class OrganizationDomainModel implements Serializable {
/**
* Value used to link an identity provider with all domains from the organization.
* If the user's email domain matches any of the organization domains, automatic redirection will be performed.
*/
public static final String ANY_DOMAIN = "ANY";
/**
* Creates a new domain model with the given name (unverified by default)
* @param name the domain name
*/
public OrganizationDomainModel(String name);
/**
* Creates a new domain model with the given name and verification status
* @param name the domain name
* @param verified whether the domain is verified
*/
public OrganizationDomainModel(String name, boolean verified);
/**
* Returns the domain name
* @return the domain name
*/
public String getName();
/**
* Returns whether the domain is verified
* @return true if verified, false otherwise
*/
public boolean isVerified();
}Event interfaces for organization membership lifecycle management.
public interface OrganizationModel {
/**
* Base interface for organization membership events
*/
interface OrganizationMembershipEvent extends ProviderEvent {
OrganizationModel getOrganization();
UserModel getUser();
KeycloakSession getSession();
}
/**
* Event fired when a user joins an organization
*/
interface OrganizationMemberJoinEvent extends OrganizationMembershipEvent {
static void fire(OrganizationModel organization, UserModel user, KeycloakSession session);
}
/**
* Event fired when a user leaves an organization
*/
interface OrganizationMemberLeaveEvent extends OrganizationMembershipEvent {
static void fire(OrganizationModel organization, UserModel user, KeycloakSession session);
}
}import org.keycloak.organization.OrganizationProvider;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.OrganizationDomainModel;
import org.keycloak.models.KeycloakSession;
public class OrganizationManagementExample {
public void createOrganization(KeycloakSession session) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
// Create a new organization
OrganizationModel org = provider.create("Example Corp", "example-corp");
org.setDescription("Example Corporation for demonstrating organization management");
org.setEnabled(true);
// Add domains to the organization
Set<OrganizationDomainModel> domains = new HashSet<>();
domains.add(new OrganizationDomainModel("example.com", true)); // verified domain
domains.add(new OrganizationDomainModel("example.org", false)); // unverified domain
org.setDomains(domains);
// Set custom attributes
Map<String, List<String>> attributes = new HashMap<>();
attributes.put("industry", Arrays.asList("Technology"));
attributes.put("size", Arrays.asList("Enterprise"));
org.setAttributes(attributes);
}
public void findOrganizationByDomain(KeycloakSession session, String email) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
// Extract domain from email
String domain = email.substring(email.indexOf("@") + 1);
// Find organization by domain
OrganizationModel org = provider.getByDomainName(domain);
if (org != null) {
System.out.println("Found organization: " + org.getName());
}
}
}public class OrganizationMembershipExample {
public void manageMembership(KeycloakSession session, RealmModel realm) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
OrganizationModel org = provider.getByAlias("example-corp");
// Add a managed member (lifecycle bound to organization)
UserModel managedUser = session.users().getUserByEmail(realm, "employee@example.com");
if (managedUser != null) {
provider.addManagedMember(org, managedUser);
}
// Add an unmanaged member (existing user joining organization)
UserModel unmanagedUser = session.users().getUserByEmail(realm, "partner@otherdomain.com");
if (unmanagedUser != null) {
provider.addMember(org, unmanagedUser);
}
// Search members with filters
Map<String, String> filters = new HashMap<>();
filters.put(UserModel.SEARCH, "john");
filters.put(MembershipType.NAME, MembershipType.MANAGED.name());
Stream<UserModel> managedMembers = provider.getMembersStream(org, filters, false, 0, 10);
managedMembers.forEach(user -> {
System.out.println("Managed member: " + user.getEmail());
});
// Get member count
long memberCount = provider.getMembersCount(org);
System.out.println("Total members: " + memberCount);
}
public void checkMembershipStatus(KeycloakSession session, UserModel user) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
// Find all organizations the user belongs to
Stream<OrganizationModel> userOrganizations = provider.getByMember(user);
userOrganizations.forEach(org -> {
boolean isManaged = provider.isManagedMember(org, user);
System.out.println("User is " + (isManaged ? "managed" : "unmanaged") +
" member of " + org.getName());
});
}
}public class OrganizationIdentityProviderExample {
public void associateIdentityProvider(KeycloakSession session, RealmModel realm) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
OrganizationModel org = provider.getByAlias("example-corp");
// Get an identity provider
IdentityProviderModel idp = realm.getIdentityProviderByAlias("corporate-saml");
if (idp != null) {
// Associate identity provider with organization
boolean associated = provider.addIdentityProvider(org, idp);
if (associated) {
// Configure email-based routing
Map<String, String> config = idp.getConfig();
config.put(OrganizationModel.IdentityProviderRedirectMode.EMAIL_MATCH.getKey(), "true");
idp.setConfig(config);
realm.updateIdentityProvider(idp);
System.out.println("Identity provider associated with organization");
}
}
// List all identity providers for the organization
Stream<IdentityProviderModel> orgIdps = provider.getIdentityProviders(org);
orgIdps.forEach(identityProvider -> {
System.out.println("Organization uses IDP: " + identityProvider.getAlias());
});
}
}public class OrganizationSearchExample {
public void searchOrganizations(KeycloakSession session) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
// Search by name or domain
Stream<OrganizationModel> searchResults = provider.getAllStream("example", false, 0, 10);
searchResults.forEach(org -> {
System.out.println("Found: " + org.getName() + " (" + org.getAlias() + ")");
});
// Search by attributes
Map<String, String> attributes = new HashMap<>();
attributes.put("industry", "Technology");
Stream<OrganizationModel> attrResults = provider.getAllStream(attributes, 0, 10);
// Get organization count
long totalOrgs = provider.count();
System.out.println("Total organizations: " + totalOrgs);
// Check if organizations are enabled in the realm
boolean orgEnabled = provider.isEnabled();
System.out.println("Organizations enabled: " + orgEnabled);
}
}Organization management provides a comprehensive framework for multi-tenant scenarios, enabling domain-based user routing, federated identity integration, and hierarchical membership management within Keycloak realms.
Install with Tessl CLI
npx tessl i tessl/maven-org-keycloak--keycloak-server-spi