You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: iOS sign-in saves the guessed mail.<domain> host instead of the server the redirects lead to #95
When you sign in with an email address, the app checks /.well-known/jmap on https://<domain>, https://mail.<domain> and https://webmail.<domain>. It saves the name it tried for whichever one answered (src/lib/server-discovery.tsL194 at 0.1.86). When mail.<domain> only redirects to the real mail server, as it often does for a second domain on the same server, the account is saved with that redirect name.
Steps to Reproduce
Host example.org as an extra domain on a Stalwart server at mail.example.com, with https://mail.example.org redirecting (302) to https://mail.example.com.
In the iOS app, sign in as user@example.org.
Look at the server URL saved for the account.
Expected Behavior
The account is saved with https://mail.example.com, the server that actually answered. _jmap._tcp.example.org SRV, ua-auto-config and the session document's apiUrl all name that host.
Actual Behavior
The account is saved with https://mail.example.org. Every request then takes the redirect, which may drop the Authorization header when iOS follows it to another domain, and the operator has to keep the redirect name and its TLS certificate alive for as long as the account exists.
The current main (19720d6) behaves the same way. probeJmapServer returns only a boolean, and discoverServerForEmail keeps the candidate name it tried (server-discovery.ts L210-L217). The login screen saves that value as the server URL (LoginScreen.tsx L378-L384).
This also explains why every request goes through the redirect. rewriteSessionUrls moves the session's apiUrl onto the origin of the saved server URL (jmap-client.ts L884-L896, rewriteSessionUrl). So the session document's apiUrl on mail.example.com gets replaced with the mail.example.org origin.
Some details that matter when choosing among your proposed fixes:
Option 2 (apiUrl) does not work when the server answers 401. The probe counts a 401 as a hit without reading a body (L161), so there is no session document to read.
Option 1 (response.url) only covers the plain fetch path. With a client certificate set, secureFetch follows a redirect only to the same host or from http to https of that host. Any other 3xx comes back unfollowed (client-cert.ts L219-L247, mayFollowRedirect). The probe then sees a non-200 status and returns false (L162). For those users, mail.example.org isn't discovered at all.
9b28c00 on main changes this. When the probe of a guessed name is answered on another host, the app now offers and saves that host: for your example the confirm step shows https://mail.example.com and the account is stored with it. A base path in front of /jmap/session is kept. A redirect that ends on plain http is not taken over.
With a client certificate set, the request does not follow a redirect to another host, so the probe now takes that one step itself. It carries no credentials.
Not in this change: the SRV lookup and ua-auto-config. An account that was already saved with the redirect name keeps it until you sign in again.
This is covered by unit tests only. I could not run it on iOS against a setup like yours, so I am leaving the issue open. Could you confirm with the next release?
I ran this on iOS against a setup like the one in the report. The build was the branch of #142, which is main at b3b4230 (0.1.94 plus the commits since, 9b28c00 among them) with one change to the tab bar; the sign-in code is the same as on main.
Setup: an iPhone 17 Pro simulator on iOS 26.5, a Release build. mail.<second domain> answered every request with a 302 to the same path on the real mail server, a Stalwart 1.1.0 on another domain; the bare second domain answered 404. The redirecting host logged each request it received.
Signing in with "Use my email address" and user@<second domain>: the confirm step showed the real mail server, not mail.<second domain>.
After a password sign-in, Settings > Account showed the real server as the JMAP server.
The redirecting host saw two requests in all, the discovery probes of /.well-known/jmap on the bare domain and on mail., neither with an Authorization header. Nothing came through it after sign-in.
Not covered: OAuth sign-in, a client certificate (the path where the probe takes the redirect step itself), and a real device.
This shipped in 0.1.93 (9b28c00), and wmcparla confirmed it on iOS against a redirect setup like yours: the confirm step and the saved account show the real server, and nothing goes through the redirect host after sign-in. An account saved with the redirect name before the update keeps it until you sign in again. Closing this; if you still see the guessed host on 0.1.93 or later, reopen it.
Activity
bulwark-warden commentedon Nov 8, 2026
The current main (
19720d6) behaves the same way.probeJmapServerreturns only a boolean, anddiscoverServerForEmailkeeps the candidate name it tried (server-discovery.ts L210-L217). The login screen saves that value as the server URL (LoginScreen.tsx L378-L384).This also explains why every request goes through the redirect.
rewriteSessionUrlsmoves the session'sapiUrlonto the origin of the saved server URL (jmap-client.ts L884-L896,rewriteSessionUrl). So the session document'sapiUrlonmail.example.comgets replaced with themail.example.orgorigin.Some details that matter when choosing among your proposed fixes:
apiUrl) does not work when the server answers 401. The probe counts a 401 as a hit without reading a body (L161), so there is no session document to read.response.url) only covers the plainfetchpath. With a client certificate set,secureFetchfollows a redirect only to the same host or from http to https of that host. Any other 3xx comes back unfollowed (client-cert.ts L219-L247,mayFollowRedirect). The probe then sees a non-200 status and returns false (L162). For those users,mail.example.orgisn't discovered at all._jmap._tcpSRV" (docs/parity/01-auth-accounts.md L175).Until this is fixed, you can avoid the guessed host by entering the real server yourself through "I know my server address" (EmailStep.tsx L93-L95).
Bulwark Warden is an AI assistant and can be wrong. It answers from the docs, the code and earlier issues, and links its sources.
fix: save the mail server the sign-in redirects lead to, not the gues…
rathlinus commentedon Nov 9, 2026
9b28c00 on main changes this. When the probe of a guessed name is answered on another host, the app now offers and saves that host: for your example the confirm step shows https://mail.example.com and the account is stored with it. A base path in front of /jmap/session is kept. A redirect that ends on plain http is not taken over.
With a client certificate set, the request does not follow a redirect to another host, so the probe now takes that one step itself. It carries no credentials.
Not in this change: the SRV lookup and ua-auto-config. An account that was already saved with the redirect name keeps it until you sign in again.
This is covered by unit tests only. I could not run it on iOS against a setup like yours, so I am leaving the issue open. Could you confirm with the next release?
wmcparla commentedon Nov 14, 2026
I ran this on iOS against a setup like the one in the report. The build was the branch of #142, which is
mainat b3b4230 (0.1.94 plus the commits since, 9b28c00 among them) with one change to the tab bar; the sign-in code is the same as onmain.Setup: an iPhone 17 Pro simulator on iOS 26.5, a Release build.
mail.<second domain>answered every request with a 302 to the same path on the real mail server, a Stalwart 1.1.0 on another domain; the bare second domain answered 404. The redirecting host logged each request it received.user@<second domain>: the confirm step showed the real mail server, notmail.<second domain>./.well-known/jmapon the bare domain and onmail., neither with anAuthorizationheader. Nothing came through it after sign-in.Not covered: OAuth sign-in, a client certificate (the path where the probe takes the redirect step itself), and a real device.
🤖 Generated with Claude Code
rathlinus commentedon Nov 15, 2026
This shipped in 0.1.93 (9b28c00), and wmcparla confirmed it on iOS against a redirect setup like yours: the confirm step and the saved account show the real server, and nothing goes through the redirect host after sign-in. An account saved with the redirect name before the update keeps it until you sign in again. Closing this; if you still see the guessed host on 0.1.93 or later, reopen it.