Skip to content

Overview

A mail server has to know which users exist and how to check their passwords. Many organisations already keep that information somewhere: in an LDAP server, in an SQL database or in an identity provider. Stalwart can manage the accounts itself, or it can take them from one of these existing systems, so that the accounts and passwords an organisation already maintains serve for mail as well.

The system that holds this information is called a directory. A directory is the source of truth for the accounts of a domain: it decides which accounts and addresses exist, and it verifies their passwords. Stalwart has its own directory, the internal directory, in which accounts are created and managed on the server. An external directory is one of the other systems, which the server queries instead.

Whatever the directory, every account is also stored on the server as an Account object. The server needs this local account because mailboxes, quotas, roles and settings belong to the server, not to the directory. With an external directory, the server creates the local account the first time the directory returns it, and updates it from the directory afterwards.

The choice follows from where the accounts are managed today. The directories also differ in how they check passwords, and in whether they can answer, for an incoming message, if a recipient address exists:

DirectoryUse whenPassword checkRecipient lookup
InternalStalwart is the only place accounts are managedOn the serverOn the server
LDAPAccounts live in OpenLDAP, Active Directory or another LDAP serverBind as the user, or comparison with the hash in the directoryLDAP search
SQLAccounts live in a database table, for example of a hosting control panelComparison with the hash returned by a querySQL query
OpenID ConnectAccounts live in an identity provider that issues OAuth tokensNone: clients sign in at the provider and present its tokensNone

Two of these differences decide the choice more often than the others.

The first is two-factor authentication. The internal directory is the only one that supports two-factor authentication, so accounts of LDAP and SQL directories are never asked for a one-time code.

The second is the recipient lookup. An OpenID Connect directory cannot be queried for an address, which means that its accounts exist on the server only after they have signed in once. Mail for an account that has never signed in is therefore rejected.

Some deployments need the identity provider to create, suspend and delete accounts itself. For these, combine the directory with SCIM provisioning, a protocol through which the identity provider pushes account changes to the server. In a domain opened to SCIM, the directory then only verifies sign-ins, and it creates or updates no accounts. Provisioning models compares the two approaches.

An external directory does not supply every property of an account. It supplies some, and the server keeps the rest:

PropertySource
Whether an account or address exists (LDAP, SQL)Directory
Password checkDirectory
Display nameDirectory, overwritten at each synchronisation
AliasesDirectory, added at each synchronisation; aliases removed from the directory are kept on the server
Group membershipsDirectory, replaced at each synchronisation; missing groups are created
Roles, permissions, quotas, tenant, Sieve scripts and settingsServer
App passwords, API keys and OAuth grantsServer

Because the server keeps its own copy of each account, removing an entry from the directory never deletes the account or its data. The account is deleted only when an administrator deletes it, or when SCIM does.

For a domain served by an LDAP or SQL directory, the directory alone decides which recipient addresses exist. An address is accepted only if the directory returns an account or group for it. A few kinds of address are still accepted without the directory: mailing lists, masked addresses and catch-all addresses defined on the server, and addresses of relay domains. Accounts and aliases that exist only on the server are not accepted.

The server caches positive answers from the directory for a short time. As a consequence, an entry deleted from the directory may still receive mail briefly.

External directories are Directory objects, with one variant per kind: Ldap, Sql and Oidc. In the WebUI they are found under Settings › Authentication › Directories.

Several directories can be configured, but one of them serves every domain that does not name its own. This default directory is selected by directoryId on the Authentication singleton (found in the WebUI under Settings › Authentication › General). When directoryId is empty, the internal directory serves those domains.

Changes to Directory objects and to directoryId take effect after a ReloadSettings action.

Whatever directory is configured, the recovery administrator is always authenticated by the server itself. It therefore remains available when an external directory fails (Recovery mode).

A server that hosts several organisations may need a different directory for each of them. A domain can therefore use a directory other than the default by setting its own directoryId, which takes precedence over Authentication.directoryId. One server can thus serve one domain from Active Directory, another from an identity provider and a third from the internal directory.

With several directories, each sign-in has to reach the directory of its domain. Password sign-ins are routed by the domain of the user name. Bearer tokens without a user name are routed by the claims of the token.

A further check keeps the directories apart. After a token of an external OpenID Connect directory has been validated, the domain of the account must be served by that same directory. Without this check, one provider’s tokens could sign in accounts of another provider’s domain. When the check fails, the sign-in fails, and the auth.failed event records the reason The account's domain is not served by the directory that authenticated it.

A directory can also belong to a tenant through its memberTenantId. In that case it counts against the tenant’s maxDirectories quota.