Skip to content

SMTP XOAUTH2 authentication ignores the configured SMTP user #13667

Description

@Rikdekker

Is your feature request related to a problem? Please describe.

When an account uses OAuth (xoauth2), the SASL XOAUTH2 identity for SMTP is hardcoded
to the account's email address:

https://github.com/nextcloud/mail/blob/main/lib/SMTP/SmtpClientFactory.php#L78-L81

$params['xoauth2_token'] = new Horde_Smtp_Password_Xoauth2(
    $account->getEmail(),
    $decryptedAccessToken,
);

The configured SMTP user (smtp_user) is passed to Horde as username, but for XOAUTH2
the identity that actually gets used is the one inside the token object, so the setting
has no effect. For password auth the SMTP user is honoured, and provisioning can
already derive a distinct SMTP user via buildSmtpUser(), so the xoauth2 path is the
only place where this configuration is silently ignored.

Practical impact: sending from a Microsoft 365 shared mailbox is impossible.

Exchange Online treats the two protocols differently:

Protocol XOAUTH2 identity sent Result
IMAP shared@example.org Works — this is what Microsoft documents for shared mailboxes
SMTP shared@example.org 535 5.7.3 Authentication unsuccessful

A shared mailbox has no sign-in credentials of its own, so it cannot be an SMTP AUTH
identity. Microsoft documents the userName substitution for shared mailboxes
without noting that it does not apply to SMTP, and a Microsoft engineer confirms the
limitation in this Q&A thread
describing exactly this symptom (IMAP fine, SMTP 535 5.7.3):

Yes, we cannot use shared mailbox for SMTP AUTH client submission.

The supported approach is to authenticate as the delegate's own mailbox and let
Exchange Send As handle the sender address. Thunderbird does exactly this, which is why
users report that Thunderbird can send from a shared mailbox while Nextcloud Mail cannot.
Mail cannot do it today because the two identities cannot be separated.

Describe the solution you'd like

Use the configured SMTP user as the XOAUTH2 identity when it is set, falling back to the
account's email address otherwise:

$xoauth2User = $mailAccount->getOutboundUser();
if (empty($xoauth2User)) {
    $xoauth2User = $account->getEmail();
}

This is backwards compatible by construction: the account form already requires
smtpUser in the OAuth flow and pre-fills it with the email address, so for existing
accounts both values are identical and nothing changes. Only users who deliberately
configure a different SMTP user see different behaviour.

IMAPClientFactory should stay as it is — there the shared mailbox address is the
correct identity, and that path works today.

Describe alternatives you've considered

  • Adding the shared mailbox as an alias on the personal account. This works and is
    what we currently advise, but the sent message is stored in the personal Sent folder,
    because the sent mailbox is bound to the account rather than the alias. Colleagues
    sharing the functional mailbox do not see the replies, which defeats the purpose of a
    shared mailbox.
  • Fixing it in Exchange. Not possible; there is no tenant or app registration setting
    that makes a shared mailbox a valid SMTP AUTH identity.

Additional context

This surfaced in the SURF Works pilot, where Nextcloud is offered to Dutch research and
education institutions. Functional/shared mailboxes are widely used there, so this
affects more organisations than just the one that reported it.

Related but distinct: #12223 (admin-managed shared mailbox access) would still need this
fix underneath it to work with Microsoft 365. #6524 and #7723 cover the IMAP and
in-Nextcloud delegation sides respectively.

There is precedent for decoupling the auth identity from the account address in this
codebase: #12442 (Dovecot master user support) does the same thing across the IMAP, SMTP
and Sieve client factories.

I have verified this end to end against a real Exchange Online shared mailbox (Full
Access + Send As granted to the personal account): IMAP works, SMTP is refused while the
shared mailbox is the XOAUTH2 identity, and sending succeeds as soon as the personal
mailbox is used instead — with the sent message landing in the shared mailbox's own Sent
Items folder.

I have a patch with unit tests ready and will open a PR.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions