Hi,
We've hit a bit of a snag while setting up our one page js client. Changing the value
of the "sub" claim to anything other than the unique identifier of the keycloak
user causes the keycloak adapter to detect the changes to the session and clear out the
tokens, forcing the users to re-log in after every 5 seconds.
We are using the version 2.3.0 of keycloak. Our app is set up to use keycloak.js adapter
for all things related to OIDC. The adapter is configured to use the "code
authorization" (standard) flow. The instance of keycloak is configured to use an
external OIDC identity provider and the users are uniquely identified by their e-mails.
Naturally, we wanted that the "sub" claim in the claim set returned by calling
the keycloak's OIDC /token endpoint would return the unique identity of the external
user rather than the internal identifier of the keycloak user, so we re-configured the
keycloak client by adding a property mapper to map the user's email to the
"sub" claim, here the example of the access token:
{
"sub": "user(a)company.com",
"iat": 223235098325,
"email": "user(a)company.com",
...
}
Once we had implemented these changes on the keycloak side, our users were able to
initially sign into the application, but when they tried to access any functionality
within the app, they would be prompted to sign in again. The problem seems to related to
the OIDC session management and the assumption and the "sub" claim always
matches the keycloak user's unique identifier.
We narrowed the problem down to four components:
- keycloak.js
- login-status-iframe.html
-
services\srv\main\java\org\keycloak\protocol\oidc\endpoints\LoginStatusIframeEndpoint.java
- services\src\main\java\org\keycloak\services\managers\AuthenticationManager.java
In keycloak.js, line 637, the implementation creates a session id to be used to check the
session state. Notice that the code uses the value from the "sub" claim:
var sessionId = kc.realm + "/" + kc.tokenParsed.sub;
In AuthenticationManager.createLoginCookie, line 306, the value of the
"SESSION_COOKIE" is set to:
String sessionCookieValue = realm.getName() + "/" + user.getId();
Sadly, in our configuration, the value returned of by user.getId() is not the same as the
value stored in the "sub" claim, thus causing the session management code in
login-status-iframe.html, line 53 to clear out any tokens and force the users to re-login
the next time it checks the session state (default is 5 second intervals):
var cookie = getCookie();
if (sessionState == cookie) { ... } else { callback("changed"); }
Looking at the LoginStatusIframeEndpoint.preCheck (LoginSatusIframeEndpoint.java, lines
71-93), we've noticed that the implementation does not even make use of the user
identity, only the session id.
The workaround, at least temporary, for us was to add the "id" claim containing
the user identity internal to keycloak, and modify the keycloak JS adapter code to look
for the "id" claim and use its value instead of the value in the "sub"
claim when creating the session id, i.e.:
var sessionId;
if (kc.tokenParsed.id) {
sessionId = kc.realm + "/" + kc.tokenParsed.id;
} else {
sessionId = kc.realm + "/" + kc.tokenParsed.sub;
}
Is this a bug, or does it work as intended, i.e. the users should never set the
"sub" claim to anything other than the keycloak's user identity? If this is
a bug, I can submit a JIRA request and a fix as long as the workaround above seems like an
acceptable solution
Any comments are welcome
Regards,
Peter