Skip to content

feat: OIDC/OAuth2 authentication integration for Horde base#110

Open
jcdelepine wants to merge 2 commits into
horde:FRAMEWORK_6_0from
jcdelepine:feat/OidcIntegration
Open

feat: OIDC/OAuth2 authentication integration for Horde base#110
jcdelepine wants to merge 2 commits into
horde:FRAMEWORK_6_0from
jcdelepine:feat/OidcIntegration

Conversation

@jcdelepine

Copy link
Copy Markdown
Contributor
Wires the OIDC auth driver (horde/Core) into the Horde base application:

- OAuthLoginController: accept GET in addition to POST to support
  automatic redirect from login.php to the IdP.

- login.php: auto-redirect to the configured OIDC provider when
  auth.driver = oidc and auth.params.redirect_provider is set.
  Redirects on all logout reasons except explicit auth failures
  (REASON_BADLOGIN, REASON_FAILED, REASON_LOCKED).

- LoginServiceFactory: register OidcPreLogoutHandler as a pre-logout
  handler when the auth driver is oidc.

- LoginService::performLogout(): aggregate redirect URLs from
  pre-logout handlers; handler redirect takes priority over
  redirect_on_logout config.

- OAuthAccountController: resolve a human-readable Horde username from
  OIDC userinfo claims (preferred_username, uid, login, email);
  store tokens for XOAUTH2 hooks; filter login.php as post-login
  redirect destination.

- OAuthTokenRepositoryFactory: SQL-backed token repository.

- Admin UI: OIDC-specific provider fields (logout strategy, SLO
  endpoints, XOAUTH2 domain).

- conf.xml: OIDC auth driver configuration and OAuth/OIDC token
  storage tab.

- Migration 3: add OIDC-specific columns to horde_oauth_providers.

- hooks.php.dist: smtp_credentials and backchannel logout route.

Depends on:
- horde/Core: OIDC integration (PR #160)
- horde/base refactor/login-logout-delegate-to-service (PR #109)

Jean Charles Delépine added 2 commits June 12, 2026 18:31
login.php handled logout logic directly (CSRF check, clearAuth,
session setup, redirect). LoginService.performLogout() already
encapsulates all of this. Delegate to it so logout behaviour is
consistent whether the request comes from login.php or from the
modern routing stack (ResponsiveLogoutController).

Pre-logout handlers registered in LoginService now run on all
logout paths, not just those going through the responsive stack.
Wires the OIDC auth driver (horde/Core) into the Horde base application:

- OAuthLoginController: accept GET in addition to POST to support
  automatic redirect from login.php to the IdP.

- login.php: auto-redirect to the configured OIDC provider when
  auth.driver = oidc and auth.params.redirect_provider is set.
  Redirects on all logout reasons except explicit auth failures
  (REASON_BADLOGIN, REASON_FAILED, REASON_LOCKED).

- LoginServiceFactory: register OidcPreLogoutHandler as a pre-logout
  handler when the auth driver is oidc.

- LoginService::performLogout(): aggregate redirect URLs from
  pre-logout handlers; handler redirect takes priority over
  redirect_on_logout config.

- OAuthAccountController: resolve a human-readable Horde username from
  OIDC userinfo claims (preferred_username, uid, login, email);
  store tokens for XOAUTH2 hooks; filter login.php as post-login
  redirect destination.

- OAuthTokenRepositoryFactory: SQL-backed token repository.

- Admin UI: OIDC-specific provider fields (logout strategy, SLO
  endpoints, XOAUTH2 domain).

- conf.xml: OIDC auth driver configuration and OAuth/OIDC token
  storage tab.

- Migration 3: add OIDC-specific columns to horde_oauth_providers.

- hooks.php.dist: smtp_credentials and backchannel logout route.

Depends on:
- horde/Core: OIDC integration (PR ##160)
- horde/base refactor/login-logout-delegate-to-service (PR horde#109)
@ralflang ralflang self-requested a review June 15, 2026 09:51
@ralflang ralflang self-assigned this Jun 15, 2026
@ralflang

Copy link
Copy Markdown
Member

As anticipated in #109 - This is somewhat more complicated to handle than the previous PRs on the Oauth library itself.

Different architectures

Your model of integrating OIDC works a bit different than what we started to implement. I understand yours already works correctly for your install and delivers OAUTH (for UI login) and XOAUTH (for backend login) and a seamless integration.

That makes it a bit hard for me to defend wanting to do things a bit different.

Fundamentally, the upstream model makes OAuth 2.0 / OIDC an alternative to an Auth driver. Your model makes it an auth driver. In our model, OIDC capabilities (beyond "login to horde") can be added to apps and services individually and independent of login method. I.e. we can drive service login to Twitter, Github, Mastodon, Google Calendar ... independent of how to login to Horde. At the moment many of the necessary changes in horde are not yet implemented, i.e. activesync and caldav/carddav backends with OAuth 2.0 support.

Gaps when OIDC is the auth backend

  • User listing/searching
  • activesync (at least for now)
  • caldav/carddav (at least for now)
  • JSON-RPC & SOAP
  • LDAP without an "admin" bind credential.

Cherry Picking

I would like to take a lot of your PRs' supporting code because it does mostly what we need to do across both models.
Other parts I would like to rework piecemeal to fit into both models

Username

As an opt-in mechanism creating/matching an auth backend username to an attribute provided by the IdP would also work in the upstream concept.

Compromise on OAuth Login driver

I see there's room for the OAuth Login driver as the primary/only login mechanism for some installations. Horde's login.php will forward to the IdP screen transparently. This will however hide mode selection and language selection as well as the secondfactor UI.

Timeline

I already stretched the Horde 6.0 Stable Release timeline for some last minute fixes to the cache and activesync codebase. I would rather not stretch much further.

@TDannhauer How do you see this? This could easily be an addition for an early horde/base 6.1 as it's a major feature.

@TDannhauer

Copy link
Copy Markdown
Contributor

To be honest, I prefer a soon release of Horde.
On large scale, H6 is overdue for years, and I think we layed a good foundation to continue with bugfixes and further larger changes will be then in 6.1, might it be Auth topics or ActiveSync.

Reading the techn question being a Auth driver or beeing an Alternative to an with driver: what is the technical difference? Is adding another "Module Type" worth it?

@jcdelepine

Copy link
Copy Markdown
Contributor Author

@ralflang @TDannhauer Thank you for the detailed feedback. I understand the architectural
concerns and agree that keeping OAuth orthogonal to the auth driver
is the cleaner long-term approach.

Looking at what actually blocked me: when logging in via OAuth with a
non-OIDC auth driver, IMP returned "User is not authorized for
Mail". That error is what led me to write the OIDC auth driver in the
first place.

The root cause is that IMP_Auth::_canAutoLogin() only attempts XOAUTH2
when auth.driver = oidc. A minimal change — trying OAuth2 tokens first
regardless of the auth driver, falling back to hordeauth when none are
available — would allow IMP to work with LDAP/SQL/IMP auth drivers while
OAuth tokens are present. Ingo/Sieve already works this way through the
transport_credentials hook, without any special auth driver.

This would allow the hook infrastructure (OidcHookHelper,
imap_preauthenticate, smtp_credentials, transport_credentials) to land
independently of the auth driver question. The OIDC auth driver,
OidcPreLogoutHandler and the backchannel logout controller can wait for
when the broader session invalidation mechanism is ready.

Does this direction make sense to you?

@ralflang

Copy link
Copy Markdown
Member

Yes, this direction makes sense to me. Allow me some time to look into how we can best integrate what your hooks do with the federated approach.

@TDannhauer There is quite some difference.

With OIDC as the "auth driver" it will be responsible for all aspects of user handling and everything OIDC naturally cannot do will be unaccessible. With the model we designed for, we can have both "real users of this platform" and "collaborators/privileged guests". We can also allow social login from different platforms (github, google, university internal OIDC) while still having one specific account.

The main problem is we historically used a concept of "application auth" (which delegates nicely to horde auth most of the time) which defined both if the user can use the application and how to login to backends (smtp, imap, sieve, ldap to some degree, gollem samba backends, ...) and increasingly this is model doesn't fit very well.

These days many services and resources horde needs to access live in cloud platforms which don't want username/password style logins. They want Oauth2.0/OIDC (everything google calendar/tasks/adressbook, social media, billing platforms) or XOauth (Imap, SMTP, ... ) which is OAuth but funneled into a traditional protocol's password field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants