Skip to content
Advertisement

Keycloak desktop java adapter deletes KEYCLOAK_IDENTITY cookies

I’ve got a problem with Keycloak java adapter. I try to integrate desktop application with Keycloak and enable SSO between a few other web applications. The problem is that when I try to login to Keycloak everything works perfect and smooth, I get information about proper authentication, obtain token and can even parse it without any problem, but there is no session created in WebBrowser (no session, no cookies). This means that I can’t use just created session with other apps in same Keycloak realm, even if session in Keycloak is created properly.

What’s more cookies created and stored earlier by other applications are also deleted (as cookies I mean KEYCLOAK_IDENTITY and KEYCLOAK_INDENTITY_LEGACY) after “succesful” login attempt with my desktop adapter. When I inspect browser cookies, there is some warning statement saying that cookies are rejected cause of their expiration.

What I use is KeycloakInstalled adapter (in latest, 15.0.2 version). I configured it using instruction on the page: https://www.keycloak.org/docs/latest/securing_apps/

The most important piece of code in this case in my opinion:

            KeycloakInstalled keycloak = new KeycloakInstalled();
            AdapterConfig config = new AdapterConfig();
            Map<String, Object> credentials = new HashMap<String, Object>();
            credentials.put("secret", secret);
            config.setAuthServerUrl(url);

            keycloak.getDeployment().setRealm(realm);
            keycloak.getDeployment().setAuthServerBaseUrl(config);
            keycloak.getDeployment().setResourceName(resource);
            keycloak.getDeployment().setResourceCredentials(credentials);
            keycloak.getDeployment().setClientAuthenticator(ClientCredentialsProviderUtils.bootstrapClientAuthenticator(keycloak.getDeployment()));

            keycloak.loginDesktop();

In this case some Keycloak properties are set statically in keycloak.json file and some dynamically in Java (example above). In keycloak.json file, some properties like realm, auth-server-url, resource and secret are filled with junk data, just to be, because they are set later dynamically.

{
  "realm": "<realm>",
  "auth-server-url": "<url>",
  "ssl-required": "external",
  "resource": "<keycloak-client>",
  "use-resource-role-mappings": true,
  "credentials" : {
    "secret" : "abc"
  },
  "truststore" : "<file>.jks",
  "truststore-password" : "<password>"
}

Keycloak’s client configuration I’ve set like this:

Keycloak_configuration

How can I avoid deleting session cookies with my desktop adapter?

Advertisement

Answer

I’ve managed my problem. The thing was all about adapter authentication mechanism. It works like that:

  1. Adapter connects to keycloak to authenticate user.
  2. Then there is a redirection to address in parameter from first url marked as redirect_url to tell the adapter that authentication was positive. Note that to this moment every cookie is set correctly.
  3. Then there is another redirection to the /delegated endpoint which make all session cookies expired. It’s all due to this fragment of code in Keycloak repository:
    @Path("delegated")
    public Response kcinitBrowserLoginComplete(@QueryParam("error") boolean error) {
        AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), clientConnection);
        AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), clientConnection);
        ...
    }

source: https://github.com/keycloak/keycloak/blob/d29d945cc4f5674ecff58cf5bf6bb65933f65cad/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java#L295

What I’ve done was creating my own CustomKeycloakInstalled class extending original KeycloakInstalled in which I copied key method loginDesktop() and some other necessary methods to let loginDesktop() work properly and also – what was most important, overrode function returning last redirecting url (/delegated) with my own:

        private String getRedirectUrl() {
            String redirectUrl = CustomKeycloakInstalled.this.getDeployment().getTokenUrl()
                    .replace("/protocol/openid-connect/token", "/my-endpoint");
            if (this.error != null) {
                redirectUrl = redirectUrl + "?error=true";
            }

            return redirectUrl;
        }

Now every cookie was set properly and SSO worked perfectly, but there was no /my-endpoint in Keycloak server so the last step was to implement and deploy it. I used KeycloakResourceProvider example under: https://github.com/keycloak/keycloak/tree/master/examples/providers/rest

It’s really simple so what I did was replacing get() method with the implementation of Keycloak’s endpoint delegated but without expiring cookies fragment:

    @GET
    @Produces("text/plain; charset=utf-8")
    public Response get(@QueryParam("error") boolean error) {
        if (error) {
            LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
            return forms
                    .setAttribute("messageHeader", forms.getMessage(Messages.DELEGATION_FAILED_HEADER))
                    .setAttribute(Constants.SKIP_LINK, true).setError(Messages.DELEGATION_FAILED).createInfoPage();

        } else {
            LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
            return forms
                    .setAttribute("messageHeader", forms.getMessage(Messages.DELEGATION_COMPLETE_HEADER))
                    .setAttribute(Constants.SKIP_LINK, true)
                    .setSuccess(Messages.DELEGATION_COMPLETE).createInfoPage();
        }
    }

Then compile it and deploy in Keycloak.

Advertisement