Per credential type directory dispatch: fallback directory for password binds under OIDC

Follow up from OIDC authentication while keeping offline recipient resolution, moving the enhancement side here as suggested.

Context

We run Stalwart Enterprise as the mail backend for email.eu, a sovereign EU workspace. Internal directory is the authoritative account and recipient source, principals are provisioned through the management API, and Authentik is the IdP. The topology that works today:

  • OIDC directory as the active directoryId for bearer token validation, offline via JWKS
  • Internal registry consulted for recipient resolution, since the OIDC directory reports it cannot resolve recipients
  • App passwords validated against the internal store before the active directory

This is confirmed working. Inbound RCPT resolves to pre created principals including never authenticated accounts, app password IMAP binds work, and bearer auth is fully offline.

The gap

With OIDC as the active directory, plain basic auth binds are rejected with Unsupported credentials type for OIDC backend. So every IMAP, SMTP and DAV client on every device needs an app password, even though the user already has one IdP password. That per device app password management is the single blocker to rolling this out to non technical users.

Request

Extend the existing per credential dispatch to plain password binds. Stalwart already routes app passwords and API keys differently from the active directory, so the pattern exists. Keep directoryId pointed at OIDC for bearer tokens, but allow an optional fallback directory (LDAP or internal) that handles basic auth credentials. Something like:

directory."oidc".fallback = "internal"   # or an ldap directory id

Order would be: app password store, then fallback directory for plain binds, then active directory for bearer. This makes single password IMAP/SMTP/DAV work without giving up offline recipient resolution or app password support.

Two smaller items from the same setup

  1. Disable or scope OIDC auto provisioning. A validly signed token whose username claim matches no principal auto creates an account with no tenant assignment. Since we provision exclusively through the management API, we would like to disable auto provisioning, or scope it to a memberTenantId.

  2. Hard fail or retry on OIDC discovery failure. If the discovery fetch fails at startup the directory is dropped with a registry.build-error and the server starts with a dangling directoryId. Bearer auth then 401s while internal passwords still validate, which silently changes auth semantics on a security boundary. An option to hard fail startup, or retry discovery, would make the failure mode explicit.

Happy to test any of this against our email.eu cluster.