opencloud icon indicating copy to clipboard operation
opencloud copied to clipboard

Getting logged out after access token expires while refresh token is still valid.

Open jeffreyvangorkum opened this issue 2 months ago • 4 comments

Describe the bug

Getting logged out after access token expires while refresh token is still valid. Even if you have activity

Steps to reproduce

  1. Go to opencloud url and authenticate on Authentik IDP
  2. After successful authentication, use opencloud / wait for 5 minutes and get logged out.
  3. Need to authenticate again

Expected behavior

My access token is valid for 5 minutes but my refresh token is valid for 30 days. If the access token fails the introspect, I expect the refresh token to be used to retrieve a new access token but instead a logout is requested.

Actual behavior

After 5 minutes, I get redirected back to my OIDC IDP to authenticate again.

Setup

Running the docker compose with Authentik as OIDC IDP.

Request that Opencloud makes after 5 minutes (i've masked the URL's):

https://sso.domain.com/application/o/opencloud/end-session/?id_token_hint=<access_token>&post_logout_redirect_uri=https%3A%2F%2Fopencloud.domain.com%2F

Additional context

If I change the token lifetime in Authentik to 1 minute, I'll be logged out after 1 minute, no matter if there is activity or not.

jeffreyvangorkum avatar Oct 01 '25 22:10 jeffreyvangorkum

I think you're suffering from https://github.com/opencloud-eu/opencloud/issues/1493

This is likely unrelated to the refresh_token. AFAIK the web client doesn't even request the offline_access scope. So depending on the concrete IDP it might not even get a refresh_token.

rhafer avatar Oct 02 '25 10:10 rhafer

Not too sure, as there isn't a http401 error in the logs. There isn't anything in the logs really, just a http301 redirect to the IDP logout endpoint exactly after the access token expires.

jeffreyvangorkum avatar Oct 02 '25 12:10 jeffreyvangorkum

This also affects the default IDP (not only external IDP):

When using WEB_OIDC_SCOPE="openid profile email offline_access", a refresh token is issued and saved in the LocalStorage. However 5 minutes after closing the browser (IDP session expires) the user is required to log in again.

I'd expect OpenCloud to use its refresh token to acquire a new access token. Re-login should only be necessary after 30 days of inactivity.

nicokaiser avatar Dec 03 '25 23:12 nicokaiser

When opening OpenCloud after 5 minutes, two requests fail with Status 401:

  • /api/v0/settings/roles-list
  • /graph/v1.0/me?%24expand=memberOf

Then immediately the user is being redirected to the signin page. Maybe those requests are executed before the access token was refreshed... which would make this some kind of race condition?

nicokaiser avatar Dec 04 '25 00:12 nicokaiser

FYI: I am using authentik IDP: I set my access token validity in authentik to 1 minute and my scope to: openid profile email and offline_access and then set WEB_OIDC_SCOPE=openid profile email offline_access and PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD=jwt (jwt or none depending on your setup will work with either) in the env, after that I made sure connect-src and script-src was set correctly in csp.yaml -- after that the 5 minute logout issue dissapeared -- the key seems to be the 1 minute access token validity being below the 5 minutes, using offline_access, and correcting csp.yaml

c-hicks avatar Dec 08 '25 07:12 c-hicks

But that only makes a difference when using an external IDP, right? My problem is when using the internal IDP, and this is a usecase which should by any means work out of the box.

nicokaiser avatar Dec 08 '25 08:12 nicokaiser

The default accessTokenLifespan in keycloak is set to 300 (5 minutes) in the default install, if there is any timing issues happening it's probably gonna log you out like what I was getting in Authentik -- set the accessTokenLifespan = 120 which is 2 minutes, restart and see if it fixes it -- my best guess is the issue might be with the cache/token expiry aligning and other checks firing at the five minute mark, with the access token set to two minutes or one minute like I did, the issue should dissapear or happen very very rarely if at all, hopefully it gets fully fixed in an update, in my case I also had to set offline_access which should be set by default in the opencloud keycloak install.

c-hicks avatar Dec 08 '25 08:12 c-hicks

I'm not sure we are talking about the same thing. When OpenCloud is open (browser is open) the token gets refresh via timer, also using the refresh_token (when offline_access is enabled).

My problem is when reopening OpenCloud after some time: access_token seems to be used even if the client knows it has expired and the refresh_token is not used even if it's there and valid:

  1. [authService:initializeContext] - updating context with saved access_token index.html-CGFTrQAi.mjs:37:51950
  2. XHR GET https://opencloud.example.com/graph/v1.0/me?$expand=memberOf [HTTP/3 401 13ms]
  3. XHR POST https://opencloud.example.com/api/v0/settings/roles-list [HTTP/3 401 14ms]
  4. Object { message: "Request failed with status code 401", name: "AxiosError", code: "ERR_BAD_REQUEST", config: {…}, request: XMLHttpRequest, response: {…}, status: 401, stack: "", … }
  5. Object { message: "Request failed with status code 401", name: "AxiosError", code: "ERR_BAD_REQUEST", config: {…}, request: XMLHttpRequest, response: {…}, status: 401, stack: "", … }
  6. https://opencloud.example.com/signin/v1/identifier/_/authorize?client_id=web&redirect_uri=https%3A%2F%2Fopencloud.example.com%2Foidc-callback.html&response_type=code&scope=openid+profile+email+offline_access&state=0f7215df0021442787445ab11c79df76&code_challenge=Sg0d3-YssViXB4cWLg14Mu06daPwtpX9NW5A7F2efHs&code_challenge_method=S256&response_mode=query

Because the XHR GET and/or POST requests fail with 401 (because the expired token is used), the client then decides to redirect to Lico and OIDC login (or silent-refresh) page instead of using the refresh_token in the LocalStorage to refresh the token. Lico on the other hand has no session anymore (which is fine), so the user needs to log in again.

@c-hicks Does this work with Keycloak? E.g. log in, close the OpenCloud page, wait >5 minutes, delete all Keycloak cookies (so silent refresh will fail) and then open OpenCloud again.

nicokaiser avatar Dec 09 '25 11:12 nicokaiser

@kulmann @JammingBen Any hints from the web client POV?

micbar avatar Dec 15 '25 07:12 micbar

Hard to say without a reproducible instance to be honest. It works just fine for me with Lico.

The steps analyzed in https://github.com/opencloud-eu/opencloud/issues/1595#issuecomment-3631794731 are on point, there shouldn't be a 401 in the first place. Once that happens, the web client rightfully redirects to the login.

JammingBen avatar Dec 16 '25 10:12 JammingBen

Hard to say without a reproducible instance to be honest.

demo.opencloud.eu is a reproducible instance:

  • Open https://demo.opencloud.eu
  • Log in using the demo credentials
  • An access_token, a refresh_token and expires_at (in 5 minutes) is written to the Local Storage
  • Close the browser, wait >5 minutes
  • Delete the cookies for https://keycloak.demo.opencloud.eu (otherwise OpenCloud will use silent authentication, which does not work with Lico because Lico only uses session cookies)
  • Open https://demo.opencloud.eu again

In the console you see requests to /graph/v1.0/me?$expand=memberof and /api/v0/settings/roles-list. For both requests, the expired access_token is used despite knowing better (expires_at!).

The refresh_token is not used at all, but the user is redirected to the login (or redirected through Keycloak's silent authentication when the cookies for keycloak.demo.opencloud.eu are not deleted).

nicokaiser avatar Dec 16 '25 11:12 nicokaiser