Get redirect_uri from auth request in Custom SPI

Hi.

I’ve written a Custom User Storage Provider, which gets user related data from an external REST API. This works very well.

In my scenario I do have 1 Keycloak Server, 1 Realm and 1 Client, to which the application connects. This application is hosted 3 times, on an alpha, test and production system. Each of these systems has it’s own REST API, from which my CUSP has to get information, depending on the source of the auth request.

I actually thought that I can access the initial request to /auth from this Custom SPI in order to use the redirect_uri for matching, but I haven’t found a proper way to do so.

What I have tried to use is

log.info("getUri() 1 {}", session.getContext().getHttpRequest().getUri().getQueryParameters().get("redirect_uri"));
log.info("getUri() 2 {}", session.getContext().getHttpRequest().getUri().getPath());
log.info("getUri() 3 {}", session.getContext().getAuthenticationSession().getRedirectUri());
log.info("getUri() 4 {}", session.getContext().getConnection().getRemoteHost());
log.info("getUri() 5 {}", session.getContext().getAuthServerUrl().getQuery());
log.info("getUri() 6 {}", session.getContext().getAuthServerUrl().getPath());
session.getAttributes().forEach((s, o) -> log.info("getUri() 77 {} {}", s, o));

The idea is to check the redirect_uri, whether it contains alpha, test or www, and then use the REST API of either system.

The Custom SPI looks like this.

public class CustUserProvider implements UserStorageProvider,
        UserLookupProvider, UserQueryProvider,
        CredentialInputUpdater, CredentialInputValidator,
        UserRegistrationProvider {

    private final KeycloakSession session;
    private final ComponentModel model;
    private final CustUserClient client;

    protected Map<String, UserModel> loadedUsers = new HashMap<>();

    public CustUserProvider(KeycloakSession session, ComponentModel model) {
        this.session = session;
        this.model = model;
        this.client = new CustUserClientSimpleHttp(session, model);
    }

So I do have access to the session. I’m pretty sure that the redirect_uri is stored there. Can someone advice how to get it?

Or is there a better approach?

Thanks.

Hi,
I have a scenario (JS authenticator) where

authenticationSession.getClientNotes().get("redirect_uri");

works. In your case it could be

session.getContext().getAuthenticationSession().getClientNotes().get("redirect_uri");
1 Like

Thanks a million!!! :pray: I spent quite some time on that and did not find a solution. This works.

log.info("getUri() 1 {}", session.getContext().getAuthenticationSession().getClientNotes().get("redirect_uri"));

[cwds.custuser.provider.CustUserProvider] (executor-thread-1) getUri() 1 http://localhost:3000/

Hmm, I’ve been glad too early. For the initial /auth request it works fine, because the redirect_uri is part of the request. Now I may have enabled caching the user for some time, or not. In both cases, cache is cleared or there is no cache, the protocol/openid-connect/token request with the refresh_token fails.

[cwds.custuser.provider.CustUserProvider] (executor-thread-5) User with identifier 'osee' could not be found, response from server: java.lang.NullPointerException: Cannot invoke "org.keycloak.sessions.AuthenticationSessionModel.getClientNotes()" because the return value of "org.keycloak.models.KeycloakContext.getAuthenticationSession()" is null

This makes sense, but does not make me happy, because then the entire approach could be scraped :frowning:

The user details would need to be fetched again from the respective REST API, without having the redirect_uri for matching.

Is there any way to achieve this?
Can I add parameters to the /token request?

I cannot add something to the session or the user, because both will be gone at some point, right?

Cheers.

Can I add something to the refresh_token inside Custom SPI or Custom Token Mapper? I mean, if I could access the refresh_token I could store the origin of the initial /auth request. Does this make sense?

Hi,

do you have “1 Keycloak Server, 1 Realm and 1 Client” hosted three times (so 3 Keycloaks overall, each for every environment)? If yes I would suggest to use config properties and configure the URL of the API there.

If you are really using 1 Keycloak Server instance for all three application instances (alpha, test and production all use the same Keycloak istance? Sounds quite riskfull) maybe then you can set a session note in your CUSP (e.g. “environment=test”) and configure a “User Session Note” Mapper to include this as a claim is your token.
From my understanding this session note is accessible until the session is deleted (or you remove the value from session notes)

In our Setup we have four non-productive and one productive instance of Keycloak running. For configuration of external APIs we chose to use config properties. They are set during import of our realm.json using keycloak-config-cli.

@dominiktopp Thank you very much for your answer.

(alpha, test and production all use the same Keycloak istance? Sounds quite riskfull)

It’s like that, yes. The problem is that I have to find the actual user session, some discussion going on here: Getting UserSessionNotes returns null, while data persist

Furthermore, I have to get used to Java and the entire KeyCloak workflows. May be that my approach, implementing a User Storage Provider, is not the best way to solve that. Custom Authenticator may be the more suitable solution.