Search code examples
keycloakopenidopenresty

Keycloak: After logout, the access token can still be used


I have an nginx/openresty client to a keycloak server for authorization using openid. I am using lua-resty-openidc to allow access to services behind the proxy.

The user can access his profile at https://<my-server>/auth/realms/<real-name>/account and logout through https://<my-server>/auth/realms/<real-name>/protocol/openid-connect/logout

The problem is that, even after logout, the user can still access the services behind the server, basically it seems the token he gets from keycloak is still valid or something.... This is also a behaviour that has been observed by other users, see for example this question on how to logout from keycloak the comments from ch271828n

How can I ensure that after logout the user will no longer be able to get access until he logs in anew?


Solution

  • I had to check the lua source code, but I think I have figured the logout behaviour out: Lua-resty-openidc establishes sessions, and they are terminated when a specific url access is detected (it is controlled by opts.logout_path which we will need to be set to an address in the path of service, e.g. .../service/logout)

    In essence, there are two urls that need to be hit, one for keycloak logout, and one for openresty session logout. Accessing the keycloak logout url https://<keycloak-server>/auth/realms/<my-realm>/protocol/openid-connect/logout is done by lua after we access the opts.logout_path at https://<our-nginx-server>/service/logout

    So after setting up everything correctly, all we have to do to logout is hit https://<our-nginx-server>/service/logout. This will destroy the session and log us out.

    I think we need to set opts.revoke_tokens_on_logout to true, Also note that from my experiments, for some reason, setting up a redirect_after_logout_uri may result in the user not signing out due to redirections.

    In order to redirect to e.g. foo.bar after logout we can do the ?redirect_uri=https://foo.bar/ part. We can also redirect back to our service page, in which case it will ask for authentication anew...

    Here is an example of what we need to have for nginx.conf to make this work....

    location /myservice/ {
    
        access_by_lua_block {
            local opts = {
                redirect_uri_path = "/myservice/auth",
                discovery = "https://<keycloak-server>/auth/realms/<my-realm>/.well-known/openid-configuration",
                client_id = "<my-client-id>",
                client_secret = "<the-clients-secret>",
                logout_path = "/service/logout",
                revoke_tokens_on_logout = true,
                redirect_after_logout_uri = "https://<keycloak-server>/auth/realms/<my-realm>/protocol/openid-connect/logout?redirect_uri=https://foo.bar/",
                session_contents = {id_token=true} -- this is essential for safari!
            }
            -- call introspect for OAuth 2.0 Bearer Access Token validation
            local res, err = require("resty.openidc").authenticate(opts)
    
            if err then
                ngx.status = 403
                ngx.say(err)
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end
        }
    
        # I disbled caching so the browser won't cache the site.
        expires           0;
        add_header        Cache-Control private;
    
        proxy_pass http://my-service-server.cloud:port/some/path/;
        proxy_set_header Host $http_host;
    
        proxy_http_version 1.1;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }