Keycloak Onboarding LabChapter IV — Protocol & Login Endpoints0/5Contents
Chapter IV

Protocol & Login Endpoints

RealmsResource is the single JAX-RS entry point under /realms/{realm}/... — every sub-resource locator resolves the realm from the URL first, then hands off to a protocol endpoint (OIDC/SAML), the authorization service, or LoginActionsService, the workhorse behind every browser login step (form pages, action-token links, consent, broker callbacks). LoginActionsService funnels almost all of those endpoints through one processFlow() chokepoint that drives the AuthenticationProcessor engine, so a new engineer should read this module as 'how does an HTTP request become a call into the authentication engine'. It also shows where authorization (UMA/policy) and protocol-specific responses re-enter the same realm-scoped request pipeline.


From /realms/{realm}/... to the engines
Browser / OIDC-SAML request…ces/resources/RealmsResource.java
RealmsResource…ces/resources/RealmsResource.java
getProtocol()…ces/resources/RealmsResource.java
LoginActionsService…ces/resources/RealmsResource.java
AuthorizationService…ces/resources/RealmsResource.java
AuthenticationProcessor…esources/LoginActionsService.java
Figure I · Click a node to see what it does, where it lives in the code, and its labeled connections — the annotation opens beside it.
walkthrough

getProtocol(): dispatching to a protocol's own endpoint

services/src/main/java/org/keycloak/services/resources/RealmsResource.java · lines 108122
lines 108–110

No @GET/@POST here — this is a JAX-RS sub-resource locator, so it matches any HTTP method under {realm}/protocol/{protocol} and returns another object for JAX-RS to keep dispatching into.

line 111

First thing every locator does: resolve {realm} into a RealmModel and stash it on the session context so downstream code never has to look it up again.

lines 113–117

Looks up the pluggable LoginProtocolFactory (oidc, saml) registered under the {protocol} segment; an unknown protocol name is a 404, not a 500.

lines 119–121

Builds a per-request EventBuilder and hands off to the protocol factory to construct the actual endpoint object (e.g. OIDC's authorization/token endpoints), which JAX-RS then dispatches the real request into.

@Path("{realm}/protocol/{protocol}")public Object getProtocol(final @PathParam("realm") String name,                          final @PathParam("protocol") String protocol) {    resolveRealmAndUpdateSession(name);    LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol);    if(factory == null){        logger.debugf("protocol %s not found", protocol);        throw new NotFoundException("Protocol not found");    }    EventBuilder event = new EventBuilder(session.getContext().getRealm(), session, session.getContext().getConnection());    return factory.createProtocolEndpoint(session, event);}
step 1 of 4
@Path("{realm}/login-actions")public LoginActionsService getLoginActionsService(final @PathParam("realm") String name) {    resolveRealmAndUpdateSession(name);    EventBuilder event = new EventBuilder(session.getContext().getRealm(), session, session.getContext().getConnection());    return new LoginActionsService(session, event);}
services/src/main/java/org/keycloak/services/resources/RealmsResource.java · lines 163168
Figure II · This is the front door for the actual browser login UI: username/password forms, OTP, registration, reset-credentials, required actions, broker callbacks, and action-token links are all served by a fresh LoginActionsService built per request.
walkthrough

authenticate(): the protocol-independent login-page endpoint

services/src/main/java/org/keycloak/services/resources/LoginActionsService.java · lines 324354
lines 324–331

GET .../login-actions/authenticate is the endpoint the browser lands on after the OIDC/SAML authorization endpoint creates an authentication session and redirects into the login UI.

lines 335–338

checksForCode() re-validates the session_code/tab_id/execution combination before anything else runs — the anti-CSRF and anti-replay guard shared by every login-actions endpoint.

lines 340–341

Recovers the in-flight AuthenticationSessionModel plus whether the browser is submitting a form (an action) or just loading the page.

lines 352–354

processAuthentication resolves the realm's configured browser flow and constructs a new AuthenticationProcessor to run it — the bridge from this HTTP layer into the authentication engine.

@Path(AUTHENTICATE_PATH)@GETpublic Response authenticate(@QueryParam(AUTH_SESSION_ID) String authSessionId, // optional, can get from cookie instead                             @QueryParam(SESSION_CODE) String code,                             @QueryParam(Constants.EXECUTION) String execution,                             @QueryParam(Constants.CLIENT_ID) String clientId,                             @QueryParam(Constants.TAB_ID) String tabId,                             @QueryParam(Constants.CLIENT_DATA) String clientData) {    event.event(EventType.LOGIN);    SessionCodeChecks checks = checksForCode(authSessionId, code, execution, clientId, tabId, clientData, AUTHENTICATE_PATH);    if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {        return checks.getResponse();    }    AuthenticationSessionModel authSession = checks.getAuthenticationSession();    boolean actionRequest = checks.isActionRequest();    processLocaleParam(authSession);    return processAuthentication(actionRequest, execution, authSession, null);}protected void processLocaleParam(AuthenticationSessionModel authSession) {    LocaleUtil.processLocaleParam(session, realm, authSession);}protected Response processAuthentication(boolean action, String execution, AuthenticationSessionModel authSession, String errorMessage) {    return processFlow(action, execution, authSession, AUTHENTICATE_PATH, AuthenticationFlowResolver.resolveBrowserFlow(authSession), errorMessage, new AuthenticationProcessor());}
step 1 of 4
protected Response processFlow(boolean action, String execution, AuthenticationSessionModel authSession, String flowPath, AuthenticationFlowModel flow, String errorMessage, AuthenticationProcessor processor) {    processor.setAuthenticationSession(authSession)            .setFlowPath(flowPath)            .setBrowserFlow(true)            .setFlowId(flow.getId())            .setConnection(clientConnection)            .setEventBuilder(event)            .setRealm(realm)            .setSession(session)            .setUriInfo(session.getContext().getUri())            .setRequest(request);    if (errorMessage != null) {        processor.setForwardedErrorMessage(new FormMessage(null, errorMessage));    }    // Check the forwarded error message, which was set by previous HTTP request    String forwardedErrorMessage = authSession.getAuthNote(FORWARDED_ERROR_MESSAGE_NOTE);    if (forwardedErrorMessage != null) {        authSession.removeAuthNote(FORWARDED_ERROR_MESSAGE_NOTE);        processor.setForwardedErrorMessage(new FormMessage(null, forwardedErrorMessage));    }    Response response;    try {        if (action) {            response = processor.authenticationAction(execution);        } else {            response = processor.authenticate();        }    } catch (WebApplicationException e) {        response = e.getResponse();        authSession = processor.getAuthenticationSession();    } catch (Exception e) {        response = processor.handleBrowserException(e);        authSession = processor.getAuthenticationSession(); // Could be changed (eg. Forked flow)    }    return BrowserHistoryHelper.getInstance().saveResponseAndRedirect(session, authSession, response, action, request);}
services/src/main/java/org/keycloak/services/resources/LoginActionsService.java · lines 356395
Figure III · processFlow() is the single chokepoint every login-actions endpoint (authenticate, registration, reset-credentials, required-action) funnels through: the action flag — set from whether the browser is submitting a form vs loading a page — decides whether the AuthenticationProcessor runs authenticationAction() (advance the flow with the submitted execution) or authenticate() (render the current step).
checkpoint

Checkpoint: protocol & login endpoints

Answered in place — nothing is graded, everything is explained. 0 / 3 passed

What does every RealmsResource sub-resource locator (getProtocol, getLoginActionsService, getAuthorizationService, ...) do before anything else, and why?

In LoginActionsService.processFlow, what decides whether the AuthenticationProcessor runs authenticationAction(execution) versus authenticate()?

Why does LoginActionsService define a separate @HEAD handler (executeActionTokenHead) that just returns 200 with no body, alongside the @GET executeActionToken?