CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-keycloak--keycloak-server-spi

Service Provider Interface (SPI) contracts and abstractions for the Keycloak identity and access management server enabling extensibility through custom providers

Pending
Overview
Eval results
Files

authentication-sessions.mddocs/

Authentication Sessions

Authentication sessions manage the state during authentication flows in Keycloak. They provide a way to track user progress through multi-step authentication processes and store temporary data during login.

Core Session Interfaces

AuthenticationSessionModel

Represents an authentication session for a specific client tab.

public interface AuthenticationSessionModel extends CommonClientSessionModel {
    /**
     * Gets the tab ID for this authentication session.
     * 
     * @return tab ID
     */
    String getTabId();

    /**
     * Gets the parent root authentication session.
     * 
     * @return parent session
     */
    RootAuthenticationSessionModel getParentSession();

    /**
     * Gets the execution status map for authentication flow.
     * 
     * @return map of authenticator ID to execution status
     */
    Map<String, ExecutionStatus> getExecutionStatus();

    /**
     * Sets the execution status for a specific authenticator.
     * 
     * @param authenticator the authenticator ID
     * @param status the execution status
     */
    void setExecutionStatus(String authenticator, ExecutionStatus status);

    /**
     * Clears all execution status entries.
     */
    void clearExecutionStatus();

    /**
     * Gets the authenticated user for this session.
     * 
     * @return authenticated user or null
     */
    UserModel getAuthenticatedUser();

    /**
     * Sets the authenticated user for this session.
     * 
     * @param user the authenticated user
     */
    void setAuthenticatedUser(UserModel user);

    /**
     * Gets required actions that need to be performed.
     * 
     * @return set of required action names
     */
    Set<String> getRequiredActions();

    /**
     * Adds a required action.
     * 
     * @param action the required action name
     */
    void addRequiredAction(String action);

    /**
     * Adds a required action enum.
     * 
     * @param action the required action enum
     */
    void addRequiredAction(RequiredAction action);

    /**
     * Removes a required action.
     * 
     * @param action the required action name
     */
    void removeRequiredAction(String action);

    /**
     * Removes a required action enum.
     * 
     * @param action the required action enum
     */
    void removeRequiredAction(RequiredAction action);

    /**
     * Sets the user session note.
     * 
     * @param name the note name
     * @param value the note value
     */
    void setUserSessionNote(String name, String value);

    /**
     * Gets user session notes.
     * 
     * @return map of note names to values
     */
    Map<String, String> getUserSessionNotes();
}

RootAuthenticationSessionModel

Represents the root authentication session that can contain multiple client tabs.

public interface RootAuthenticationSessionModel {
    /**
     * Gets the session ID.
     * 
     * @return session ID
     */
    String getId();

    /**
     * Gets the realm for this session.
     * 
     * @return realm model
     */
    RealmModel getRealm();

    /**
     * Gets the timestamp when the session was created.
     * 
     * @return creation timestamp in seconds
     */
    int getTimestamp();

    /**
     * Sets the timestamp for the session.
     * 
     * @param timestamp timestamp in seconds
     */
    void setTimestamp(int timestamp);

    /**
     * Gets authentication session for a specific client and tab.
     * 
     * @param client the client
     * @param tabId the tab ID
     * @return authentication session or null
     */
    AuthenticationSessionModel getAuthenticationSession(ClientModel client, String tabId);

    /**
     * Creates a new authentication session for a client and tab.
     * 
     * @param client the client
     * @param tabId the tab ID
     * @return created authentication session
     */
    AuthenticationSessionModel createAuthenticationSession(ClientModel client, String tabId);

    /**
     * Removes authentication session for a client and tab.
     * 
     * @param client the client
     * @param tabId the tab ID
     */
    void removeAuthenticationSessionByTabId(ClientModel client, String tabId);

    /**
     * Restarts the session (clears all authentication sessions).
     */
    void restartSession(RealmModel realm);
}

CommonClientSessionModel

Base interface for client sessions with common functionality.

public interface CommonClientSessionModel {
    /**
     * Gets the session ID.
     * 
     * @return session ID
     */
    String getId();

    /**
     * Gets the realm for this session.
     * 
     * @return realm model
     */
    RealmModel getRealm();

    /**
     * Gets the client for this session.
     * 
     * @return client model
     */
    ClientModel getClient();

    /**
     * Gets the redirect URI.
     * 
     * @return redirect URI
     */
    String getRedirectUri();

    /**
     * Sets the redirect URI.
     * 
     * @param uri the redirect URI
     */
    void setRedirectUri(String uri);

    /**
     * Gets a client note.
     * 
     * @param name the note name
     * @return note value or null
     */
    String getNote(String name);

    /**
     * Sets a client note.
     * 
     * @param name the note name
     * @param value the note value
     */
    void setNote(String name, String value);

    /**
     * Removes a client note.
     * 
     * @param name the note name
     */
    void removeNote(String name);

    /**
     * Gets all client notes.
     * 
     * @return map of note names to values
     */
    Map<String, String> getNotes();

    /**
     * Gets the authentication method.
     * 
     * @return authentication method
     */
    String getAuthMethod();

    /**
     * Sets the authentication method.
     * 
     * @param method the authentication method
     */
    void setAuthMethod(String method);
}

Authentication Session Provider

AuthenticationSessionProvider

Provider for managing authentication sessions.

public interface AuthenticationSessionProvider extends Provider {
    /**
     * Creates a new root authentication session.
     * 
     * @param realm the realm
     * @return created root session
     */
    RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm);

    /**
     * Creates a root authentication session with specific ID.
     * 
     * @param realm the realm
     * @param id the session ID
     * @return created root session
     */
    RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm, String id);

    /**
     * Gets a root authentication session by ID.
     * 
     * @param realm the realm
     * @param authenticationSessionId the session ID
     * @return root session or null
     */
    RootAuthenticationSessionModel getRootAuthenticationSession(RealmModel realm, String authenticationSessionId);

    /**
     * Removes a root authentication session.
     * 
     * @param realm the realm
     * @param authenticationSession the session to remove
     */
    void removeRootAuthenticationSession(RealmModel realm, RootAuthenticationSessionModel authenticationSession);

    /**
     * Removes expired authentication sessions.
     * 
     * @param realm the realm
     */
    void removeExpired(RealmModel realm);

    /**
     * Removes all authentication sessions for a realm.
     * 
     * @param realm the realm
     */
    void onRealmRemoved(RealmModel realm);

    /**
     * Removes all authentication sessions for a client.
     * 
     * @param realm the realm
     * @param client the client
     */
    void onClientRemoved(RealmModel realm, ClientModel client);

    /**
     * Updates ownership when a client is renamed.
     * 
     * @param realm the realm
     * @param client the client
     * @param newClientId the new client ID
     */
    void updateNonlocalSessionAuthNotes(RealmModel realm, ClientModel client, String newClientId);
}

Authentication Session Compound ID

AuthenticationSessionCompoundId

Utility class for parsing authentication session compound IDs.

public class AuthenticationSessionCompoundId {
    private final String rootSessionId;
    private final String clientUUID;
    private final String tabId;

    public AuthenticationSessionCompoundId(String rootSessionId, String clientUUID, String tabId) {
        this.rootSessionId = rootSessionId;
        this.clientUUID = clientUUID;
        this.tabId = tabId;
    }

    /**
     * Parses a compound ID string.
     * 
     * @param compoundId the compound ID string
     * @return parsed compound ID
     */
    public static AuthenticationSessionCompoundId fromSessionId(String compoundId) {
        String[] parts = compoundId.split("\\.");
        if (parts.length >= 3) {
            return new AuthenticationSessionCompoundId(parts[0], parts[1], parts[2]);
        }
        throw new IllegalArgumentException("Invalid compound session ID: " + compoundId);
    }

    /**
     * Generates the compound ID string.
     * 
     * @return compound ID string
     */
    public String getEncodedId() {
        return rootSessionId + "." + clientUUID + "." + tabId;
    }

    public String getRootSessionId() { return rootSessionId; }
    public String getClientUUID() { return clientUUID; }
    public String getTabId() { return tabId; }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        AuthenticationSessionCompoundId that = (AuthenticationSessionCompoundId) obj;
        return Objects.equals(rootSessionId, that.rootSessionId) &&
               Objects.equals(clientUUID, that.clientUUID) &&
               Objects.equals(tabId, that.tabId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(rootSessionId, clientUUID, tabId);
    }

    @Override
    public String toString() {
        return getEncodedId();
    }
}

Execution Status Enum

public enum ExecutionStatus {
    SUCCESS,
    FAILED,
    SETUP_REQUIRED,
    ATTEMPTED,
    SKIPPED,
    CHALLENGED,
    FLOW_RESET
}

Usage Examples

Working with Authentication Sessions

// Create and manage authentication sessions
try (KeycloakSession session = sessionFactory.create()) {
    RealmModel realm = session.realms().getRealmByName("myrealm");
    ClientModel client = realm.getClientByClientId("my-app");
    AuthenticationSessionProvider authSessionProvider = session.authenticationSessions();
    
    // Create root authentication session
    RootAuthenticationSessionModel rootSession = authSessionProvider.createRootAuthenticationSession(realm);
    
    // Create authentication session for specific client tab
    String tabId = "tab1";
    AuthenticationSessionModel authSession = rootSession.createAuthenticationSession(client, tabId);
    
    // Set redirect URI
    authSession.setRedirectUri("https://myapp.com/callback");
    
    // Set authentication flow notes
    authSession.setNote("login_hint", "john@example.com");
    authSession.setNote("kc_locale", "en");
    
    // Track authentication progress
    authSession.setExecutionStatus("username-password-form", ExecutionStatus.SUCCESS);
    authSession.setExecutionStatus("otp-form", ExecutionStatus.CHALLENGED);
    
    // Set authenticated user after successful authentication
    UserModel user = session.users().getUserByUsername(realm, "john");
    authSession.setAuthenticatedUser(user);
    
    // Add required actions
    authSession.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
    authSession.addRequiredAction(RequiredAction.VERIFY_EMAIL);
    
    // Set user session notes for later use
    authSession.setUserSessionNote("login_ip", "192.168.1.100");
    authSession.setUserSessionNote("login_time", String.valueOf(System.currentTimeMillis()));
}

Custom Authenticator Using Authentication Session

public class CustomAuthenticator implements Authenticator {
    
    @Override
    public void authenticate(AuthenticationFlowContext context) {
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        
        // Check if user is already identified
        UserModel user = authSession.getAuthenticatedUser();
        if (user == null) {
            // Redirect to identification step
            context.failure(AuthenticationFlowError.UNKNOWN_USER);
            return;
        }
        
        // Check for previous attempts
        String attempts = authSession.getNote("custom_auth_attempts");
        int attemptCount = attempts != null ? Integer.parseInt(attempts) : 0;
        
        if (attemptCount >= 3) {
            context.failure(AuthenticationFlowError.TOO_MANY_FAILURES);
            return;
        }
        
        // Generate challenge
        String challenge = generateChallenge();
        authSession.setNote("custom_challenge", challenge);
        
        // Create challenge form
        Response challengeForm = createChallengeForm(context, challenge);
        context.challenge(challengeForm);
    }
    
    @Override
    public void action(AuthenticationFlowContext context) {
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
        
        String challenge = authSession.getNote("custom_challenge");
        String response = formData.getFirst("challenge_response");
        
        if (validateResponse(challenge, response)) {
            // Success - clear attempt count
            authSession.removeNote("custom_auth_attempts");
            authSession.removeNote("custom_challenge");
            context.success();
        } else {
            // Failed - increment attempt count
            String attempts = authSession.getNote("custom_auth_attempts");
            int attemptCount = attempts != null ? Integer.parseInt(attempts) : 0;
            attemptCount++;
            authSession.setNote("custom_auth_attempts", String.valueOf(attemptCount));
            
            if (attemptCount >= 3) {
                context.failure(AuthenticationFlowError.TOO_MANY_FAILURES);
            } else {
                Response errorForm = createChallengeForm(context, challenge, "Invalid response. Attempts: " + attemptCount);
                context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, errorForm);
            }
        }
    }
    
    private String generateChallenge() {
        // Generate random challenge
        return UUID.randomUUID().toString();
    }
    
    private boolean validateResponse(String challenge, String response) {
        // Validate the response against the challenge
        return challenge != null && challenge.equals(response);
    }
    
    private Response createChallengeForm(AuthenticationFlowContext context, String challenge) {
        return createChallengeForm(context, challenge, null);
    }
    
    private Response createChallengeForm(AuthenticationFlowContext context, String challenge, String error) {
        // Create and return form response
        return Response.ok().build(); // Simplified
    }
}

Session Management During Authentication Flow

public class MultiStepAuthenticator implements Authenticator {
    
    @Override
    public void authenticate(AuthenticationFlowContext context) {
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        
        // Check current step
        String currentStep = authSession.getNote("auth_step");
        if (currentStep == null) {
            currentStep = "step1";
        }
        
        switch (currentStep) {
            case "step1":
                handleStep1(context);
                break;
            case "step2":
                handleStep2(context);
                break;
            case "step3":
                handleStep3(context);
                break;
            default:
                context.success();
        }
    }
    
    private void handleStep1(AuthenticationFlowContext context) {
        // First step - username/password
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        
        // Store step information
        authSession.setNote("auth_step", "step1");
        authSession.setNote("step1_start_time", String.valueOf(System.currentTimeMillis()));
        
        // Challenge user for credentials
        Response form = createUsernamePasswordForm(context);
        context.challenge(form);
    }
    
    private void handleStep2(AuthenticationFlowContext context) {
        // Second step - OTP verification
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        
        // Verify step1 was completed
        if (!"step1_completed".equals(authSession.getNote("step1_status"))) {
            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
            return;
        }
        
        authSession.setNote("auth_step", "step2");
        authSession.setNote("step2_start_time", String.valueOf(System.currentTimeMillis()));
        
        Response form = createOtpForm(context);
        context.challenge(form);
    }
    
    private void handleStep3(AuthenticationFlowContext context) {
        // Final step - additional verification
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        
        // Verify previous steps
        if (!"step2_completed".equals(authSession.getNote("step2_status"))) {
            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
            return;
        }
        
        // Set completion notes for user session
        authSession.setUserSessionNote("multi_step_auth", "completed");
        authSession.setUserSessionNote("auth_duration", 
            String.valueOf(System.currentTimeMillis() - Long.parseLong(authSession.getNote("step1_start_time"))));
        
        context.success();
    }
    
    @Override
    public void action(AuthenticationFlowContext context) {
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        String currentStep = authSession.getNote("auth_step");
        
        switch (currentStep) {
            case "step1":
                if (processStep1Action(context)) {
                    authSession.setNote("step1_status", "step1_completed");
                    authSession.setNote("auth_step", "step2");
                    handleStep2(context);
                }
                break;
            case "step2":
                if (processStep2Action(context)) {
                    authSession.setNote("step2_status", "step2_completed");
                    authSession.setNote("auth_step", "step3");
                    handleStep3(context);
                }
                break;
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-keycloak--keycloak-server-spi@26.2.1

docs

authentication-sessions.md

component-framework.md

core-models.md

credential-management.md

index.md

organization-management.md

provider-framework.md

session-management.md

user-storage.md

validation-framework.md

vault-integration.md

tile.json