Single sign-on

Let your people sign in at your own identity provider — Entra ID, Okta, Keycloak, Google Workspace — instead of keeping a second password for mail.

If your organisation already has an identity provider, your people should not need a second password just for mail. With single sign-on they sign in where they always sign in, and Nixt Server issues its own tokens on the strength of that — so your corporate password never reaches IMAP, JMAP or submission at all.

Nixt Server is an OpenID Connect relying party. It works with anything that speaks OpenID Connect: Microsoft Entra ID, Okta, Keycloak, Google Workspace, Ping, and most others.

Before you start

You need two things from your provider:

  1. The issuer URL. Something like https://login.microsoftonline.com/<tenant-id>/v2.0 or https://keycloak.example.com/realms/staff. It must be https.
  2. A client id and secret, from registering Nixt Server as an application there.

When you register the application, give it this redirect URI:

https://mail.example.com/oauth/federated/callback

Ask for the openid and email scopes, and make sure the provider releases an email address — that is what Nixt Server matches against a mailbox.

Turn it on

sudo -u versealx versealx-server admin put tenants/1/federation \
  "issuer=https://keycloak.example.com/realms/staff" \
  clientId=versealx-server \
  "clientSecret=the-secret-from-your-provider"
FieldMeaning
issuerThe issuer URL, exactly as your provider spells it. Every token is checked against it.
clientIdWhat your provider knows Nixt Server as.
clientSecretThe secret that goes with it. Required the first time; leave it out later and the stored one is kept. It is never shown again.
scopesWhat to ask for. openid email if you leave it out; openid is always added.
claimWhich claim holds the mailbox address. email if you leave it out. Entra ID users often want upn.
enabledfalse configures it without switching it on.

Check what is configured at any time — the secret is never in the answer:

sudo -u versealx versealx-server admin get tenants/1/federation

The discoveryUrl in that answer is where Nixt Server will fetch your provider’s configuration from. Open it in a browser to confirm it is the right one.

Turning it off

sudo -u versealx versealx-server admin put tenants/1/federation \
  "issuer=https://keycloak.example.com/realms/staff" clientId=versealx-server enabled=false

That keeps everything you typed and stops using it. To forget it entirely:

sudo -u versealx versealx-server admin delete tenants/1/federation

What your people see

The sign-in page gains a second button: Sign in with your organisation. They type their address, press it, sign in wherever they normally do, and come back signed in.

Both ways of signing in work: the one an app like Nixt Mail uses, and the one a terminal uses when you run versealx login.

Passwords keep working. Turning on single sign-on does not take away the passwords your accounts already have — an account with one can still use it. If you want people to use the provider and nothing else, remove their passwords.

Matching people to mailboxes

The address in the provider’s token has to match a mailbox in the tenant. ada@example.com at your provider signs in to ada@example.com here.

Signing in does not create a mailbox. Somebody who exists at your provider and has no mailbox here is refused. Create accounts first, with SCIM, the admin API or the command line — those paths check who is asking and write it down, and a sign-in that quietly made accounts would do neither.

A disabled account stays disabled. If you disable or remove someone here, they do not sign in, whatever your provider says about them. That is usually the reason you disabled them.

If the provider says an address is not verified, the sign-in is refused. Set claim to a claim your provider treats as authoritative — upn on Entra ID — if its email is self-service.

A provider inside your network

Two settings in versealx-server.toml cover the on-premises case:

[federation]
allow_private = true
trust = "/etc/versealx-server/internal-ca.pem"
SettingWhen you need it
allow_privateYour provider is on a private, internal or loopback address. Off by default: the issuer is a URL a tenant administrator typed, and without this a tenant could point the server at something on your internal network.
trustYour provider’s certificate was signed by your own certificate authority rather than a public one — usual for AD FS and on-premises Keycloak. This adds a root; the certificate is still checked.

Both are node settings rather than tenant settings, so a tenant cannot grant itself either.

When a sign-in does not work

The page says only that the sign-in did not complete. That is deliberate — it is shown to whoever is standing at it, and they have not yet proved they are anyone, so it must not say whether an address exists or what happened to it.

The reason is in the server’s log:

sudo journalctl -u versealx-server -n 50 | grep -i federat
What the log saysWhat to do
it is issued for … not …The issuer in your configuration is not the one the provider publishes. Copy it exactly from the discovery document.
no key the provider publishes verifies itThe provider signs with an algorithm Nixt Server does not take. RSA and P-256/P-384 ECDSA are taken.
it carries no nonce / its nonce is not the one this sign-in asked forThe provider is not returning the nonce. Most providers need nothing set for this; check the application registration.
it carries no emailThe provider is not releasing the claim. Add the scope that releases it, or set claim to one it does release.
the provider says that address is not verifiedSee Matching people to mailboxes above.
not on the public InternetSet allow_private, above.
signed in at the provider and has no mailbox in this tenantCreate the account first.

Something unclear or out of date on this page? Tell us.