Your question
Stalwart Version: 0.16.11 (docker, community) behind caddy, no proxy protocol.
I am trying to convince stalwart to authenticate users against authelia.
Since authelia works fine for other apps, I’d assume it’s a stalwart specific issue I have.
I ended up with this configuration for authelia:
- client_id: 'stalwart-webui'
client_name: 'Stalwart'
client_secret: '$pbkdf2-sha512$310000$z3H***MLA'
public: false
authorization_policy: 'one_factor'
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://mail.example.com/account/oauth/callback'
scopes:
- 'openid'
- 'profile'
- 'email'
- 'offline_access'
response_types:
- 'code'
grant_types:
- 'authorization_code'
access_token_signed_response_alg: 'none'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
Stalwart OIDC Directory entry looks like this:
Issuer URL: https://auth.example.com
Required Audience:
Required Scopes: email, openid
Username Claim: preferred_username
Username Domain:
Name Claim: name
Groups Claim:
Tenant: (Community Version)
This brings me to a partially working thing:
1.) connect to https://mail.example.com
2.) login with user [email protected]
3.) being redirected to authelia
4.) loggin in with [email protected] + pwd
5.) confirm consent
6.) ERROR: “Failed to fetch, Back to login”
URL leading to the error: https://mail.example.com/account/oauth/callback?code=authelia_ac_uCGM0PXvz8s_***-M-fIg&iss=https%3A%2F%2Fauth.example.com&scope=openid+email+profile+offline_access&state=0a4906dc8a8dddabd7701d012d970f75
The user already exists in stalwarts’ internal database since a (working) ldap authentication is my current standard login method.
Since it’s the same ldap authelia uses, I wouldn’t expect any pwd or schema-issues.
I am pretty sure I’ve checked all available documentation, if I missed it anyway I’d be thankful for pointing me into the right direction.
Any ideas what I am doing wrong here?
I attach tracelogs, just one line has something like an error but since it appears before all the oidc-magic I am not sure wether it’s relevant.
Cheers
Michael aka /Bytewurm
2026-07-05T12:16:55Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/"
2026-07-05T12:16:55Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:55Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = (null), code = 302, size = 0
2026-07-05T12:16:55Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/account"
2026-07-05T12:16:55Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:55Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = (null), code = 302, size = 0
2026-07-05T12:16:55Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/account/"
2026-07-05T12:16:55Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:55Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = "[binary data]", code = 200, size = 465
2026-07-05T12:16:55Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/logo"
2026-07-05T12:16:55Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:55Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = (null), code = 302, size = 0
2026-07-05T12:16:55Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/logo/"
2026-07-05T12:16:55Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:55Z DEBUG Resource not found (resource.not-found) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824
2026-07-05T12:16:55Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = "{"type":"about:blank","status":404,"title":"Not Found","detail":"The requested resource does not exist on this server."}", code = 404, size = 120
2026-07-05T12:16:59Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/api/discover/stalwart_test%40example.com"
2026-07-05T12:16:59Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:16:59Z TRACE Write batch operation (store.data-write) elapsed = 0ms, total = 2
2026-07-05T12:16:59Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = "{"issuer":"https://auth.example.com","jwks_uri":"https://auth.example.com/jwks.json","userinfo_endpoint":"https://auth.example.com/api/oidc/userinfo","token_endpoint":"https://auth.example.com/api/oidc/token","authorization_endpoint":"https://auth.example.com/api/oidc/authorization","scopes_supported":["offline_access","openid","profile","email","address","phone","groups"],"claims_supported":["amr","aud","azp","client_id","exp","iat","iss","jti","rat","auth_time","nonce","groups","sub","name","given_name","family_name","middle_name","nickname","preferred_username","profile","picture","website","email","email_verified","alt_emails","gender","birthdate","zoneinfo","locale","phone_number","phone_number_verified","address","updated_at"],"code_challenge_methods_supported":["S256"]}", code = 200, size = 772
2026-07-05T12:17:06Z DEBUG HTTP connection started (http.connection-start) listenerId = "https", localPort = 443, remoteIp = 127.0.0.1, remotePort = 37754
2026-07-05T12:17:06Z DEBUG HTTP request URL (http.request-url) listenerId = "https", localPort = 443, remoteIp = 127.0.0.1, remotePort = 37754, url = "/healthz/live"
2026-07-05T12:17:06Z TRACE Expression evaluation result (eval.result) listenerId = "https", localPort = 443, remoteIp = 127.0.0.1, remotePort = 37754, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:17:06Z TRACE HTTP response body (http.response-body) listenerId = "https", localPort = 443, remoteIp = 127.0.0.1, remotePort = 37754, contents = "{"type":"about:blank","title":"OK","status":200,"detail":"OK"}", code = 200, size = 62
2026-07-05T12:17:06Z DEBUG HTTP connection ended (http.connection-end) listenerId = "https", localPort = 443, remoteIp = 127.0.0.1, remotePort = 37754, elapsed = 4ms
2026-07-05T12:17:09Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, url = "/account/oauth/callback?code=authelia_ac_uCGM0PXvz8s__DQ8E3mYbLP7PkQ24dxp9bL-JZhg4qw.ue59bW7epDWV_SQ4FuwlaQZ66PXjJ6OpYaZQp-M-fIg&iss=https%3A%2F%2Fauth.example.com&scope=openid+email+profile+offline_access&state=0a4906dc8a8dddabd7701d012d970f75"
2026-07-05T12:17:09Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, id = "Http with id singleton", key = "allowedEndpoints", result = "Integer(200)"
2026-07-05T12:17:09Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 172.22.0.7, remotePort = 35824, contents = "[binary data]", code = 200, size = 465
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