gogatekeeper / gatekeeper

An OpenID / Proxy service
https://gogatekeeper.github.io/gatekeeper/
Apache License 2.0
253 stars 43 forks source link

Tokens are not refreshed when access token is expired #469

Closed transducer closed 1 month ago

transducer commented 1 month ago

Tokens are not refreshed when access token is expired

Summary

Even when enabling refresh tokens (enable-refresh-tokens=true), refreshing the token fails. When calling a backend Gatekeeper with no-redirects=true with an expired access token (obtained from cookie set by a frontend Gatekeeper) the logging used to roughly show

info accces token for user has expired, attemping to refresh the token
info injecting the refreshed access token cookie

but now it logs

error 401 Unauthorized

Environment

Server: Keycloak 24.0.4 Gatekeeper: 2.11.0

Expected Results

When a Gatekeeper with no-redirects=true is called with a refresh token and expired access token, the access token should be refreshed using the refresh token.

Actual Results

The server returns a 401 Unauthorized.

Steps to reproduce

  1. Have a website protected by a Gatekeeper that sets a domain cookie with the following settings

    
     --discovery-url=https://{URL_LOGIN}/realms/{REALM}
     --client-id=xx
     --client-secret={CLIENT_SECRET}
     --redirection-url={REDIRECTION_URL}
     --listen=0.0.0.0:{PORT}
     --upstream-url={UPSTREAM_URL}
     --encryption-key=123
     --enable-refresh-tokens=true
     --cookie-domain={COOKIE_DOMAIN}
  2. From this website call a backend (on the same domain) protected by a Gatekeeper with the same confidential client and similar settings as above (only different {UPSTREAM_URL} and possible {PORT}) with the addition --no-redirects=true

Additional Information

This used to work in at least version 2.3.1.

transducer commented 1 month ago

@p53 thank you. At the moment also in version 2.11.0 with no-redirects=true you can use cookies or an authorization header, both work and I think both should work. For example, if the backend consists of a WebSocket it's impossible to use an authorization header, since headers cannot be used with WebSockets, but cookies do work.

The architecture I describe resembles the Backend for Frontend [BFF] architecture as described in the IETF's recommendations for OAuth 2.0 for Browser-Based Applications, where "[t]he BFF manages OAuth access and refresh tokens in the context of a cookie-based session, avoiding the direct exposure of any tokens to the JavaScript application". In this architecture encrypted HttpOnly cookies with the refresh and access tokens can be stored in the browser.

If an API with no-redirects=true cannot refresh the token in the HttpOnly cookie, the only way I see to do so is by refreshing the page so that the Frontend's Gatekeeper is used to refresh the access token and put it in the HttpOnly cookie. This is not a good user experience in a SPA. Is refreshing the page the recommended approach or is there another way? Or could the functionality of refreshing the HttpOnly cookies from a Gatekeeper with no-redirects=true be reintroduced?

p53 commented 1 month ago

Sorry I will check code, I didn't have time to check it in work, when making comment, probably I removed only part related to refreshing during refactoring, there are a lot of historical things, you are right this frontend and websocket use case should be covered, probably for user experience it would be better to reintroduce it altought I don't like idea of getting token through one gatekeeper and refreshing it on different from architecture point it seems not right and clean, will think about it

transducer commented 1 month ago

Thanks for all your efforts!

p53 commented 1 month ago

@transducer in 2.9.3 there is new option enabled by default --enable-idp-session-check=true, this might cause your 401, you need to set it to false => --enable-idp-session-check=false, can you try it as i checked i didn't remove cookie refresh code (altough i was thinking about it) so i guess this might be causing your issue, if it won't solve your issue, could you post error logs, thx

transducer commented 1 month ago

@p53 thank you! I will check tomorrow and let you know.

transducer commented 1 month ago

Yes, this works.