I am doing a custom REST endpoint which should be called from a required action template (so, not with a JWT token but with a “pure” keycloak cookie)
In the ftl template, I am trying to do :
tmp = await fetch("/auth/realms/myrealm/myproviderid/mypath", {method:'POST'})
It correctly sends the request with a cookie AUTH_SESSION_ID (and also AUTH_SESSION_ID_LEGACY with same value)
The custom endpoint is correctly triggered
In my resource, how can I access the corresponding current user ?
I’ve tried the following but they all return null :
UserSessionModel userSession = new AuthenticationSessionManager(session).getUserSessionFromAuthCookie(session.getContext().getRealm());
UserModel user = session.getContext().getAuthenticationSession().getAuthenticatedUser()
AuthResult auth = new AppAuthManager().authenticateIdentityCookie(session, session.getContext().getRealm());
Thanks for any pointer
I reply to myself in case this can help someone in the future
First, it is usually not correct to use the REST API from a required action template
The correct thing to do is to take care of multiple actions inside processAction
@Override
public void processAction(RequiredActionContext context) {
String action = context.getHttpRequest().getDecodedFormParameters().getFirst("action");
if ("action1".equals(action)) {
//do the logic for the action1
if (success) {
context.success();
} else {
context.challenge(context.form().setError("xxx").createForm("my.ftl"));
}
} else if ("action2".equals(action)) {
//do the logic for action2 (in this example, the action always request the challenge again)
context.challenge(context.form().createForm("my.ftl"));
} else {
//by default, requst the challenge again
context.challenge(context.form().createForm("my.ftl"));
}
}
in the ftl file, you can then do
<form id="kc-action1-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<input type="hidden" name="action" value="action1" />
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="save" id="kc-submit" type="submit" value="${msg("action1")}"/>
</form>
<form id="kc-action2-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<input type="hidden" name="action" value="action2" />
<input class=""
name="save" id="kc-submit" type="submit" value="${msg("action2")}"/>
</div>
</form>
This will correctly call the corresponding if block of the processAction method
Now, if you REALLY want to call a custom REST API from inside keycloak page (without a JWT), here is what you can do
@POST
@NoCache
@Path("")
@Produces(MediaType.APPLICATION_JSON)
public Response xxx() {
//
//!\ this method can be called ONLY from a keycloak page (NOT with a JWT token)
//
//get the realm, client and tabId from the context (/!\ for client, can not use directly context.getClient(), it is strangely always null...)
RealmModel realm = session.getContext().getRealm();
String clientId = session.getContext().getUri().getQueryParameters().getFirst("client_id");
ClientModel client = clientId != null ? session.clients().getClientByClientId(realm, clientId) : null;
String tabId = session.getContext().getUri().getQueryParameters().getFirst("tab_id");
if (realm == null || clientId == null || client == null || tabId == null) {
System.out.println("bad request realm = " + realm + ", clientId = " + clientId + ", client = " + client + ", tabId = " + tabId);
return Response.status(400).build();
}
//find the corresponding authentication session
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm, client, tabId);
if (authSession == null || authSession.getAuthenticatedUser() == null) {
System.out.println("no auth");
return Response.status(401).build();
}
UserModel user = authSession.getAuthenticatedUser();
//the rest of your code
2 Likes