Search code examples
djangokeycloakopenid-connectopenid

How to detect the logged in user from Keycloak OpenID logout_token on back channel logout?


First let me describe the setup:

We have a frontend Angular based product from a different client team (not part of code we can easily modify), and a backend django based API server.

The front end logs in to a keycloak server, and when logged in, the backend gets an Auth header with a bearer token in every request. From this, we are able to identify the logged in user as follows (using python-keycloak):

ret = keycloak.userinfo(bearer_token)
username = ret['preferred_username']

This is obviously very wasteful since it needs an extra network request to keycloak everytime - so we create a django user session instead and use that for session management.

Now when it comes to logging out, when the user logs out from the front end, we need to void the django session.

I've setup the "Back channel logout URL" on the keycloak realm settings to call some endpoint on the django server. The endpoint gets called on logout, and it gets a "logout_token" value in the arguments.

Now I'm not sure how I am supposed to identify which user is logging out based on this token. How can this be done?

Thanks in advance...


Solution

  • I am not 100% sure about the soundness of you architecture. Nonetheless, regarding your particular question:

    ret = keycloak.userinfo(bearer_token) 
    username = ret['preferred_username']
    

    From the Keycloak book:

    preferred_username This is the username of the authenticated user. You should avoid this as a key for the user as it may be changed, and even refer to a different user in the future. Instead, always use the sub field for the user key.

    From the OpenID connect specification one can read that:

    OPs send a JWT similar to an ID Token to RPs called a Logout Token to request that they log out. ID Tokens are defined in Section 2 of [OpenID.Core].

    The following Claims are used within the Logout Token:

    iss REQUIRED. Issuer Identifier, (...)

    sub OPTIONAL. Subject Identifier,(...)

    aud REQUIRED. Audience(s), (...)

    iat REQUIRED. Issued at time, (...)

    jti REQUIRED. Unique identifier for the token, (...) (...) sid OPTIONAL. Session ID - String identifier for a Session. This represents a Session of a User Agent or device for a logged-in End-User at an RP. (..)

    A Logout Token MUST contain either a sub or a sid Claim, and MAY contain both. If a sid Claim is not present, the intent is that all sessions at the RP for the End-User identified by the iss and sub Claims be logged out.

    So you can try to use the claim 'sub', which is the unique identifier of the authenticated user. You would (probably) need to create the mapping between 'sub' and 'user' in your backend. Alternatively, you could use the same logic but applying it to 'sid' instead. There you would map the ID session of Keycloak to your own ID session.

    However, my question is:

    The front end logs in to a keycloak server, and when logged in, the backend gets an Auth header with a bearer token in every request.

    From that bearer token (which I am assuming to be an access token) can't you simply get 'preferred_username' (or better the 'sub' claim) from there? that would not require any extra call to the Keycloak server.