nextcloud / nextcloud/user_oidc

TokenInvalidatedListener ends the whole IdP SSO session on local token expiry — no way to disable

Open
#1,518 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
181
Forks
60
Avg merge
7h 34m
Merged PRs (30d)
26

Description

Which version of user_oidc are you using?

8.6.1 (behavior unchanged on current main)

Describe the issue

lib/Listener/TokenInvalidatedListener.php unconditionally performs an RP-initiated logout at the IdP (server-side GET to the provider's end_session_endpoint with id_token_hint) whenever a Nextcloud auth token is invalidated.

Nextcloud invalidates browser login tokens when they exceed session_lifetime (default 24h since last activity on that token). When that happens, this listener destroys the user's entire IdP SSO session — logging them out of every other application connected to the same IdP realm (in our case Keycloak: portal, chat, webmail, docs...), even though the IdP's own session policy (e.g. 30-day ssoSessionIdleTimeout) says the session should live on.

Observed in production: Keycloak LOGOUT event, client=nextcloud-app, originating from the Nextcloud pod IP, almost exactly session_lifetime after the user's login — followed immediately by every other RP's refresh tokens failing with invalid_grant (Session not active). From the user's perspective, logging into Nextcloud once starts a ~24h timer after which they are logged out of the whole platform.

This is surprising because:

  • The IdP is the authority on SSO session lifetime; a single RP's local token expiry silently overrides realm policy for all other RPs.
  • Other OIDC clients (Roundcube, oauth2-proxy, portals, etc.) do not end the IdP session when their local token expires — user_oidc is the odd one out.
  • There is no admin setting to opt out: the listener only skips when the OIDC session row is missing, the IdP session is already marked closed, or no end_session_endpoint exists (and blanking the endpoint would also break user-initiated logout redirect).
Expected behavior

An admin-configurable switch (app config or per-provider setting, e.g. logout_from_idp_on_token_invalidation) controlling whether token invalidation triggers the IdP end_session call — or defaulting to not ending the IdP session on mere local token expiry (as opposed to explicit user logout, where RP-initiated logout is appropriate and expected).

We currently patch the listener in our deployment to skip only the end_session HTTP call (keeping the session-row cleanup); happy to submit a PR adding a proper config flag if maintainers agree on the desired shape/default.

Steps to reproduce
  1. Nextcloud behind Keycloak via user_oidc, default session_lifetime (24h); IdP realm SSO idle/max set to e.g. 30 days.
  2. Log into Nextcloud via OIDC; also log into another app on the same realm (same SSO session).
  3. Wait for the NC auth token to pass session_lifetime and be invalidated (background job or next request).
  4. The IdP session is terminated: the other app's refresh token now fails with invalid_grant (Session not active); IdP logs a LOGOUT initiated from the Nextcloud server IP.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading lib/Listener/TokenInvalidatedListener.php and trace how token invalidation reaches the IdP end_session call. Check the existing app or provider configuration patterns before choosing the switch shape and default. Done means local session-row cleanup still occurs while the IdP logout call can be disabled for token expiry, without breaking explicit user-initiated logout.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
authentication
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.