OIDC as a Directory; getting 'NetworkError when attempting to fetch resource.' / 'Failed to fetch'

Issue Description

When I input my username / email, I’m forwarded to my identity provider (Keycloak), I sign in (despite already being validated earlier which defeats the purpose of SSO, I think).

Expected Behavior

It would redirect me and log me in

Actual Behavior

It takes me back to my mail server and it says, ‘NetworkError when attempting to fetch resource.’ on Firefox, ‘Failed to fetch’ on Google Chrome.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

OIDC

Additional Context

I thought it could be CORS related but I’m not sure.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

That error is coming from the browser, not from Stalwart, which is why there is nothing in your server log. With an OIDC directory the discovery call hands the browser Keycloak’s own endpoints, so the code-for-token exchange is a direct cross-origin POST from the admin UI to Keycloak’s token endpoint. A “NetworkError” or “Failed to fetch” there means the browser blocked or could not make that request; an actual OAuth failure would come back as a token exchange error with a status code instead.

The usual cause is CORS on the Keycloak side, since a new client’s Web Origins is empty by default. Add your Stalwart origin there (or +). While you are in that client, it also needs to be public with client authentication off, PKCE set to S256, https://yourhost/oauth/callback as a valid redirect URI, and the client id stalwart-webui. The other cause with an identical message is the browser simply not being able to reach the issuer host, so split DNS or an untrusted certificate on Keycloak. The Network tab in devtools tells the two apart at a glance: a CORS block looks quite different from a failed connection.

Being prompted to sign in again despite an active session is expected today, the login flow always sends prompt=login. One thing to watch once the exchange succeeds: the audience requirement defaults to stalwart, so the Keycloak client needs an audience mapper emitting that value, or the field changed to match what your tokens carry.