feat(provisioning): authenticate provisioned accounts with the user's OIDC token (XOAUTH2)#13311
feat(provisioning): authenticate provisioned accounts with the user's OIDC token (XOAUTH2)#13311LeahAuroraV wants to merge 4 commits into
Conversation
… OIDC token (XOAUTH2) Add an oidcEnabled option to provisioning configurations. Accounts are provisioned with auth_method=xoauth2 and no stored password: - The user_oidc login token is mirrored into the account row on interactive requests (ExternalTokenRequestedEvent) and refreshed at the IdP token endpoint with the stored refresh token for background jobs, mirroring the Google/Microsoft integrations. - SMTP client creation dispatches a new BeforeSmtpClientCreated event so tokens are also refreshed before sending. - The option is guarded end to end: the admin UI toggle, provisioning validation and the integration itself require the user_oidc app to be installed and enabled. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Leah <leahvenema@hotmail.com>
Document the oidcEnabled provisioning option end to end: the Authentik provider + federation setup, the Dovecot oauth2 passdb and Postfix submission config, the Nextcloud occ/provisioning steps, verification (including the background refresh path) and troubleshooting. Includes a ready-to-run local Dovecot + Postfix pair for the docker-dev environment. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Leah <leahvenema@hotmail.com>
|
Thanks for the PR |
ChristophWurst
left a comment
There was a problem hiding this comment.
Quick architectural review
Thank you
| * The IdP token endpoint and client credentials are read from the user_oidc provider | ||
| * configuration; this integration is inert when user_oidc is not installed. | ||
| */ | ||
| class OidcIntegration { |
There was a problem hiding this comment.
To manage the complexity of this change I'd favor getting individual OIDC support in first (and close #12491), and then build OIDC provisioning on top.
There was a problem hiding this comment.
Thanks for the quick review!
So basically create an oauth client within the mail app in which users can choose OpenID as option when adding a mailbox, with a well-known discovery url (or the individual endpoints that would contain), client id and client secret, and then have the open id authentication open in a new tab?
Then the provisioning on top would be defining those urls, client id and secret in the admin settings, rather than using the the user_oidc app.
There was a problem hiding this comment.
Or would the clients always be global settings?
There was a problem hiding this comment.
We already have XOAUTH2 support for Google and Microsoft. Admins set the client id and secret. When a user sets up an account we detect if oauth should be used based on the IMAP server.
For OIDC I could imagine the same. Admin sets client id, secret and server hostname. When a user then creates a new account for that hostname it will open the auth popup for that server.
| */ | ||
| public function isAvailable(): bool { | ||
| return $this->appManager->isEnabledForAnyone('user_oidc') | ||
| && class_exists(\OCA\UserOIDC\Event\ExternalTokenRequestedEvent::class); |
There was a problem hiding this comment.
\OCA* is the private API of another app. This is not supposed to be used by other apps to avoid inter app dependencies. The clean way is going through a OCP public API that lives in Nextcloud server and has a strong stability guarantee.
There was a problem hiding this comment.
https://github.com/nextcloud/user_oidc/blob/main/lib/Event/ExternalTokenRequestedEvent.php does specifically mention in its docblock that it is meant for external apps.
| // Psalm stubs for the optional user_oidc app (https://github.com/nextcloud/user_oidc). | ||
| // Only the surface used by OCA\Mail\Integration\OidcIntegration is declared. | ||
|
|
||
| namespace OCA\UserOIDC\Model { |
There was a problem hiding this comment.
Without a mechanism to update these stups they give a false sense of safety. Psalm will continue to pass in our repository when the user_oidc APIs change (break).
OIDC Token Yoink
This PR extends the provisioning functionality of nextcloud mail to work with access and refresh tokens obtained on login through user_oidc.
There's a new "use the user's OIDC token" toggle on a provisioning config (in the groupware section under admin settings). accounts then authenticate via XOAUTH2 with no stored password. This token is mirrored from the user_oidc (first provider) session on interactive requests, and refreshed via the stored refresh token for background/cron sync (so it keeps working after the access token expires). It's kinda similar to how the existing Google/Microsoft integrations are already working for this app. The toggle is disabled and validation rejects it when user_oidc isn't installed, and the integration will stay disabled when user_oidc is not present.
It was developed with authentik as central identity provider to which both nextcloud and dovecot/postfix are connected. Dovecot is configured in such a way that it will accept XOAUTH2 as authentication. It has been verified against the local dev setup mentioned below, as well as a more extensive testing environment that adds ldap as user directory for the mail servers to the mix.
Because this feature requires quite a big dev setup I've added documentation to get a test environment up and running: doc/oidc-xoauth2.md
Changes
OidcIntegrationservice that mirrors the user_oidc session token onto the account and refreshes it via the stored refresh token for background sync (lib/Integration/OidcIntegration.php, with Psalm stubs for the optionaluser_oidcclasses so there's no hard dependency).oidc_enabledcolumn tomail_provisionings, plus the entity/mapper field and validation (existing configs are untouched).Service/Provisioning/Manager.php,Http/Middleware/ProvisioningMiddleware.php).BeforeSmtpClientCreatedevent (IMAP already had something similar), dispatched by the SMTP factory and handled by the existing OAuth refresh listener alongside Google/Microsoft.user_oidcisn't installed (Settings/AdminSettings.php+ the two Vue components), plus unit tests for the new paths.🤖 AI (if applicable)