Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to force users to login again after their session was closed in keycloak? #477

Open
andiarbeit12 opened this issue Apr 18, 2023 · 6 comments
Labels

Comments

@andiarbeit12
Copy link

Hi!

I´m currently trying to set up Openresty as reverse proxy with Keycloak as IdP in Kubernetes.
Accessing the proxied service works as expected and I also can logout from the proxied service via /logout.

However, when I terminate the user's session in Keycloak, the user can still refresh and use the site. I want the user to have to log in again when the session is closed. How can i achieve this?

Environment
  • openresty-version: openresty/1.21.4.1
  • lua-resty-openidc version: 1.7.6-3
  • OpenID Connect provider: Keycloak: 19.0.1 - Community
Expected behaviour

Users have to log in again, when their session is closed in Keycloak.

Actual behaviour

When i sign out all active sessions in Keycloak the user can still use and refresh the proxied site without logging in again.

Configuration and NGINX server log files

default.conf:

upstream backend {
    server nginx-test:80;
}

 
server {
      listen   8080;
      root     /opt/nginx/html;

      resolver kube-dns.kube-system.svc.cluster.local;
      # disabled caching so the browser won't cache the site
      expires           0;
      add_header        Cache-Control private;
      
      lua_ssl_verify_depth 2;
      lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.pem;

      # log paths
      error_log /var/log/nginx/error.log;
      access_log /var/log/nginx/access.log;

      access_by_lua_block {
      local opts = {
          redirect_uri = "/redirect_uri",
          discovery = "<my-keycloak-url>/realms/<realm-name>/.well-known/openid-configuration",
          client_id = "<client-id>",
          client_secret = "<client_secret>",
          scope = "openid",
          redirect_uri_scheme = "https",
          logout_path = "/logout",
          redirect_after_logout_uri = "<my-keycloak-url>/realms/<realm-name>/protocol/openid-connect/logout",
          redirect_after_logout_with_id_token_hint = false,
          ssl_verify = true,
          session_contents = { id_token = true},
      }

      local res, err = require("resty.openidc").authenticate(opts)

      if err then
          ngx.status = 403
          ngx.say(err)
          ngx.exit(ngx.HTTP_FORBIDDEN)
      end

      ngx.req.set_header("X-USER", res.id_token.sub)
    }
 
location / {

    proxy_pass http://backend;
    # set headers
    # https://www.keycloak.org/server/reverseproxy
    proxy_set_header    X-Forwarded-For         $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
  }
}

Client Settings in Keycloak:

Client authentication: On
Authorization: Off
Authentication flow:
- Standard flow: On
- Direct access grants: On
- Implicit flow: Off
- Service accounts roles: Off
- OAuth 2.0 Device Authorization Grant: Off
- OIDC CIBA Grant: Off 

I appreciate any help!

@bodewig
Copy link
Collaborator

bodewig commented Apr 18, 2023

authenticate doesn't normally check whether a token is revoked before it expires as this would have a major performance impact. The usual approach is to make token lifetimes really short so the window of time between token revocation and expiration becomes small. If you can not limit token life time to an acceptable value opts.refresh_session_interval may be another option which would check whether the login is still valid on a regular basis.

@andiarbeit12
Copy link
Author

Thanks for your answer!

I'm not sure I completely understood the first suggested solution.
In Keycloak there is only an option to limit the lifespan for the access token and in the nginx.conf there are only options for the access token aswell.
Am I missing something or do I need an access_token in my session_contents to make use of the token lifetimes?

I have added the refresh_session_interval option to the local options. The user now has to log in again when opening a new tab with the protected web page after the session was terminated in Keycloak. However, the user can still use the protected page in an active tab.

@bodewig
Copy link
Collaborator

bodewig commented Apr 24, 2023

What I meant with the first option was something like "limit the access token lifespan to two minutes and just accept a token could be used for the remaining max two minutes of its lifespan even after it has been revoked". This is often an acceptable compromise.

For the second option: when refresh_session_interval elapses lua-resty-openidc will trigger a so called "silent" authentication in which the client is redirected to the authorization endpoint with parameter prompt=none which is supposed to return a new authorization code if the OIDC provider still thinks the session is valid and an error otherwise. This implies the OIDC provider knows about the user's login state. Please don't assume anybody here would know anything specific to any OIDC provider.

If you use two different tabs that share the same lua-resty-openidc session cookie than I can not explain why a fresh tab should behave any different from an already existing one. What you may be seeing could be a timing issue. lua-resty-openidc will only trigger the silent authentication redirect once the configured interval has passed since the user has last been logged in. If you log out the user this period may not have expired, yet.

@andiarbeit12
Copy link
Author

Thanks again now I get it!
I have set the refresh_session_interval = 60 and it seems like i was mistaken the user can still use the page.

nginx/error.log

2023/04/25 14:53:24 [error] 6#6: *299 [lua] openidc.lua:1098: authenticate(): unhandled request to the redirect_uri: /redirect_uri?error=login_required&state=c1f159c1e579b0bec23e8f2be60a91a8, client: 10.1.11.0, server: , request: "GET /redirect_uri?error=login_required&state=c1f159c1e579b0bec23e8f2be60a91a8 HTTP/1.1", host: "<public-url-of-my-proxied-service>"
2023/04/25 14:53:49 [error] 6#6: *547 [lua] openidc.lua:1098: authenticate(): unhandled request to the redirect_uri: /redirect_uri?error=login_required&state=7f5878339a2891c83fbe3f85e05a7dbe, client: 10.1.11.0, server: , request: "GET /redirect_uri?error=login_required&state=7f5878339a2891c83fbe3f85e05a7dbe HTTP/1.1", host: "<public-url-of-my-proxied-service>"

keycloak.log

2023-04-25 14:51:25,267 WARN  [org.keycloak.services.managers.AuthenticationManager] (executor-thread-558) Some clients have been not been logged out for user <user> in testnginx realm: nginx
2023-04-25T14:51:45.644128121Z 2023-04-25 14:51:45,642 WARN  [org.keycloak.protocol.oidc.utils.AcrUtils] (executor-thread-558) Invalid realm configuration (ACR-LOA map)
2023-04-25T14:54:45.757080442Z 2023-04-25 14:54:45,756 WARN  [org.keycloak.events] (executor-thread-558) type=LOGIN_ERROR, realmId=be5515cd-0c52-473c-b0b8-3ca9905a0413, clientId=null, userId=null, ipAddress=10.18.1.208, error=invalid_request
2023-04-25 14:54:59,818 WARN  [org.keycloak.protocol.oidc.utils.AcrUtils] (executor-thread-558) Invalid realm configuration (ACR-LOA map)
2023-04-25T14:55:40.179911220Z 2023-04-25 14:55:40,179 WARN  [org.keycloak.protocol.oidc.utils.AcrUtils] (executor-thread-558) Invalid realm configuration (ACR-LOA map)

Seems like there is a issue with in keycloak configuration. I will investigate this further.
Appreciate any suggestions!

@bodewig
Copy link
Collaborator

bodewig commented Apr 25, 2023

The nginx logs look as if silent re-authentication failed - keycloak redirects back with a "login_required" error. This looks fine.

For this calls openidc.authenticate should return a non-nil errand your code should enter the branch that ends up sending a 403 status back to the client.

@podger
Copy link

podger commented Jul 26, 2024

The nginx logs look as if silent re-authentication failed - keycloak redirects back with a "login_required" error. This looks fine.

For this calls openidc.authenticate should return a non-nil errand your code should enter the branch that ends up sending a 403 status back to the client.

I encountered the exact same issue and handled the error from the authenticate() method.

local res, err, target, session = require("resty.openidc").authenticate(opts)

if (string.find(err, "unhandled request to the redirect_uri"))
then
    if (session ~= nil) 
    then
        local ok, err = session:destroy()
        if (err ~= nil or not ok)
        then
            ngx.log(ngx.ERR, "Error destroying session: ", ok, err)
        end
    end

    return ngx.redirect(_**MY_HOME_PAGE**_) -- redirecting the home page here will result in a new redirect to login
end

I feel this is more a workaround tough.
Given the README documentation, the error resulting from the authenticate method should result in a 500 because something really went wrong. In this case we are handling an error that's contemplated in the OpenId protocol.

I would expected that this kind of error should be handled by the library itself.
At least for the require("resty.openidc").authenticate(opts) (without deny nor pass parameters) my session should be destroyed and the auth workflow reinitiated.

Without this piece code basically the user would face random 500 while navigating, page refresh wouldnt' work because most likely you are on the callback URI so the user would be stuck.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants