Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 6 additions & 127 deletions doc/oidc-xoauth2.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,134 +3,13 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

# OpenID Connect (XOAUTH2) for individual mail accounts
# OpenID Connect (XOAUTH2) local test environment

Mail can authenticate a user's IMAP/SMTP connection with an OpenID Connect (OIDC)
access token over the `XOAUTH2` SASL mechanism, instead of storing a password. This
generalises the built-in Google and Microsoft integrations to any OIDC provider
(Keycloak, Authentik, Stalwart, …).

An administrator registers one or more **OIDC providers**, each matched to accounts by
the user's **email domain**. When a user adds a mail account whose address is in a
configured domain, Mail pre-fills the mail servers and starts an interactive
authorization-code flow: the user consents at the identity provider, and Mail stores
the resulting access and refresh tokens on the account.

> This is the *individual account* flow (the user runs the consent popup themselves).
> Automatic provisioning of accounts without user interaction is a separate feature.

## How it works

```
Admin registers provider ─► email_domain + IMAP/SMTP host/port/ssl
+ client_id/secret + discovery URL + scopes
β”‚
User adds account (alice@example.com) ─► domain matches provider β†’ hosts pre-filled
β”‚
Popup β†’ provider authorization endpoint β†’ consent β†’ redirect back with code
β”‚
Server exchanges code for tokens (confidential client, secret stays server-side)
β”‚
IMAP/SMTP over XOAUTH2; the access token is refreshed automatically before it expires
```

- **Matching key: email domain.** One provider per domain. The provider is a full
connection template for that domain (separate IMAP and SMTP host/port/SSL).
- **Confidential client.** The code→token exchange runs on the server, so the client
secret is never exposed to the browser. PKCE is not required (but the flow tolerates
providers that mandate it).
- **Endpoints come from discovery.** By default only the discovery URL
(`…/.well-known/openid-configuration`) is stored; the authorization and token
endpoints are fetched from it and cached. Providers without a discovery document
can instead define these endpoints manually (see below).
- **Refresh tokens** are used to keep the connection alive for background sync, so the
default scopes include `offline_access`.

## Admin configuration

1. Open **Administration settings β†’ Groupware β†’ Mail** and find the
**OpenID Connect integration** section.
2. Click **Add OIDC provider** and fill in:
- **Display name** – free-text label.
- **Email domain** – e.g. `example.com`. Accounts with an address in this domain use
this provider.
- **IMAP** and **SMTP** – host, port and encryption (SSL/TLS, STARTTLS or none).
Defaults are `993`/SSL and `587`/STARTTLS. The two hosts may differ.
- **Discovery endpoint** – the provider's `.well-known/openid-configuration` URL.
Alternatively, tick **Manually define endpoints** and enter the **Authorization
endpoint** and **Token endpoint** directly β€” useful for identity providers that
don't publish a discovery document, or to override it. An **Introspection
endpoint** is optional (see [below](#when-the-grant-can-no-longer-be-renewed)).
When manual endpoints are used the discovery URL is ignored.
- **Client ID** / **Client secret** – credentials of the OAuth client you register
with your identity provider (see below). The secret is stored encrypted and shown
back only as a placeholder.
- **Scopes** – defaults to `openid email offline_access`.
3. **Save.**

### Registering the OAuth client with your provider

Create a confidential (web) OAuth client in your identity provider and authorize this
**redirect URL** (shown in the provider form):

```
https://<your-nextcloud>/index.php/apps/mail/integration/oidc-auth
```

Grant the client the scopes it needs to issue mail tokens and refresh tokens, and make
sure the access token it issues is accepted by your IMAP/SMTP server for `XOAUTH2`.

## User setup

1. In Mail, choose **Add mail account** and enter the email address (e.g.
`alice@example.com`).
2. If the domain matches a configured provider, the server fields are filled in and a
single sign-on notice appears. Continue.
3. A popup opens at your identity provider. Sign in and grant access.
4. The account is created and syncs over XOAUTH2 β€” no password is stored.

## Token refresh

Before each IMAP connection Mail checks whether the access token is about to expire and,
if so, refreshes it with the stored refresh token. This runs during background sync as
well, so accounts keep working without user interaction as long as the refresh token is
valid. If the provider rotates refresh tokens, the new one is stored on every refresh, so
a regularly syncing account keeps rolling its grant forward.

## When the grant can no longer be renewed

The refresh token itself eventually expires or is revoked β€” for example after an extended
outage where nothing synced, or if the provider never issued one (no `offline_access`).
OAuth defines no way to know that lifetime up front, so Mail reacts to it instead:

1. A refresh that fails is not trusted on its own β€” the provider may simply have been
unreachable. Mail asks the provider's **introspection endpoint** (RFC 7662) whether the
refresh token is still active.
2. Only a definitive `active: false` (or having no refresh token at all while the access
token has expired) marks the account as needing re-authentication. A network error or
an unavailable introspection endpoint is treated as transient and changes nothing.
3. The next time the user opens Mail, a dialog offers to **Reconnect**, which runs the
same consent popup as the initial setup and restores the account. The flag is cleared
automatically as soon as any refresh or reconnect succeeds.

> With a discovery URL, the introspection endpoint is taken from the discovery document's
> `introspection_endpoint`. With manually defined endpoints, it is used only when the
> optional **Introspection endpoint** is filled in. Without an introspection endpoint a
> failed refresh is always treated as transient and the account is never flagged
> automatically.

## Troubleshooting

- **No SSO notice when adding the account** – the email domain does not exactly match a
configured provider's *Email domain*. Matching is case-insensitive but otherwise exact.
- **Popup returns but the account is not authorized** – check the Nextcloud log
(`mail` app). Common causes: the redirect URL is not authorized at the provider, the
discovery URL is unreachable, or the client secret is wrong.
- **Authentication fails at IMAP/SMTP** – the mail server must accept the provider's
access token for `XOAUTH2`. Verify the token audience/scope expected by your mail
server.
- **Discovery changes are not picked up** – the discovery document is cached for a short
time; wait for the cache to expire or restart PHP workers.
Admin and user documentation for the generic OIDC/XOAUTH2 integration (registering a
provider, how accounts connect, troubleshooting) lives in the
[official documentation](https://docs.nextcloud.com/server/latest/admin_manual/groupware/mail.html),
not here. This file covers only how to stand up a local identity provider and mail
server pair for development, since that setup doesn't belong in the admin manual.

## Setting up a local test environment

Expand Down
Loading