Keycloak Onboarding LabChapter II — Server SPI: Provider, ProviderFactory & KeycloakSession0/5Contents
Chapter II

Server SPI: Provider, ProviderFactory & KeycloakSession

The server-spi module defines Keycloak's plugin contract: a Provider is a per-session capability instance with a single close() method, a ProviderFactory is a server-wide singleton that is discovered at boot, initialized once, and knows how to create() a Provider per session, and KeycloakSession is the request-scoped hub every subsystem calls through to obtain and cache Provider instances. Nearly every feature area in Keycloak (admin REST, model persistence, user federation, quarkus bootstrap) either implements this Provider/ProviderFactory pair or calls session.getProvider(...), so understanding the create/init/postInit/close lifecycle and the session's lazy caching is the prerequisite for reading anything else in the codebase.


public interface Provider {    void close();}
server-spi/src/main/java/org/keycloak/provider/Provider.java · lines 2327
Figure I · Provider is deliberately minimal: it only guarantees a close() lifecycle hook. Everything else a provider offers (realms(), users(), a federation lookup, a theme renderer, etc.) is declared by a sub-interface that extends Provider — the base contract just says 'I am a per-session resource that must be released'.
walkthrough

ProviderFactory: the singleton that manufactures Providers

server-spi/src/main/java/org/keycloak/provider/ProviderFactory.java · lines 3683
line 38

create(session) is the factory method: given a request-scoped KeycloakSession, it produces one Provider instance for that session. This is the only place a Provider is ever instantiated.

lines 40–42

init(config) runs exactly once per server, right after the factory is discovered, and postInit(factory) runs once all factories have been init'd — this two-phase boot lets a factory look up other providers via the session factory only after everyone is initialized.

lines 52–54

dependsOn() lets a factory declare a hard ordering requirement: the boot loader guarantees dependency factories finish postInit() (and are closed later) relative to this one, preventing use of an uninitialized provider.

public interface ProviderFactory<T extends Provider> {    T create(KeycloakSession session);    void init(Config.Scope config);    void postInit(KeycloakSessionFactory factory);    void close();    String getId();    default int order() {        return 0;    }    default Set<Class<? extends Provider>> dependsOn() {        return Collections.emptySet();    }}
step 1 of 3
    /**     * Get dedicated provider instance of provider type clazz that was created for this session.  If one hasn't been created yet,     * find the factory and allocate by calling ProviderFactory.create(KeycloakSession).  The provider to use is determined     * by the "provider" config entry in keycloak-server boot configuration. See the <a href="https://www.keycloak.org/docs/latest/server_development/index.html#_use_available_providers">Server developer guide</a> for the details.     *     *     *     * @param clazz     * @param <T>     * @return     */    <T extends Provider> T getProvider(Class<T> clazz);
server-spi/src/main/java/org/keycloak/models/KeycloakSession.java · lines 4152
Figure II · getProvider(Class) is the entry point every subsystem uses instead of newing up an implementation directly: the session resolves which concrete factory is configured, lazily creates the provider on first use, and hands back the same instance for the rest of the request.
walkthrough

How a session actually resolves and caches a provider

services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java · lines 171190
lines 171–174

The session builds a cache key from the provider's Class and delegates to the server-wide KeycloakSessionFactory to find which ProviderFactory is configured for that class.

lines 178–179

The provider is only created the first time it's asked for in this session — a per-session map (keyed by class/id) makes every subsequent getProvider() call for the same type return the identical instance.

lines 182–183

This is the one call site in the whole codebase where ProviderFactory.create(session) actually gets invoked for the plain getProvider(Class) path — everything else is a wrapper around this pattern.

    public <T extends Provider> T getProvider(Class<T> clazz) {        List<String> key = List.of(clazz.getName());        return getOrCreateProvider(key, () -> factory.getProviderFactory(clazz));    }    private <T extends Provider> T getOrCreateProvider(List<String> key, Supplier<ProviderFactory<T>> supplier) {        @SuppressWarnings("unchecked")        T provider = (T) providers.get(key);        if (provider == null) {            ProviderFactory<T> providerFactory = supplier.get();            if (providerFactory != null) {                provider = providerFactory.create(DefaultKeycloakSession.this);                providers.put(key, provider);            }        }        return provider;    }
step 1 of 3
Figure III · pipelineFrom server boot to a resolved Provider instance
5 stages
Press play — or step through the pipeline stage by stage.
How the pieces connect
Spi…va/org/keycloak/provider/Spi.java
KeycloakSession…cloak/models/KeycloakSession.java
KeycloakSessionFactory…odels/KeycloakSessionFactory.java
ProviderFactory<T>…oak/provider/ProviderFactory.java
Provider…g/keycloak/provider/Provider.java
Figure IV · Click a node to see what it does, where it lives in the code, and its labeled connections — the annotation opens beside it.