element-hq / element-hq/element-x-android
Element X (Android) — QR login (MSC4108) never sends PUT to rendezvous; both clients cancel (Android 9 / Moto Z3)
- Dominant language
- Kotlin
- Stars
- 2.4k
- Forks
- 624
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 149
Description
### Steps to reproduce
On desktop (Element Web/Desktop, Firefox 141 on Linux), logged in as @matrixadmin:matrix.example.com with a verified session.
Go to Settings → Security & Privacy → Sessions → Show QR code.
On phone (Element X 25.08.3 on Motorola Moto Z3 / Android 9), tap Sign in with QR and scan.
The flow cancels on both devices.
### Outcome
Expected: After scanning, the phone sends
PUT /_synapse/client/rendezvous/
with Content-Type: text/plain and If-Match, Synapse returns 200/204, and the device links.
Actual: Server shows only GET polling (304) from both desktop and phone; then desktop DELETEs the rendezvous (204) and subsequent GETs 404. No PUT from the phone is ever observed.
Server evidence (Synapse logs)
# GET poll from phone (Element X)
... 192.168.50.180 ... 0B 304 "GET /_synapse/client/rendezvous/01K2Z... HTTP/1.1" "Element X/25.08.3 ..."
# GET poll from desktop (Firefox)
... 192.168.50.10 ... 0B 304 "GET /_synapse/client/rendezvous/01K2Z... HTTP/1.1" "Mozilla/5.0 ..."
# Desktop cancels
... 192.168.50.10 ... 0B 204 "DELETE /_synapse/client/rendezvous/01K2Z... HTTP/1.1"
# After deletion
... 192.168.50.180 ... 45B 404 "GET /_synapse/client/rendezvous/01K2Z... HTTP/1.1"
Notice there is never a PUT /_synapse/client/rendezvous/ from the phone.
Minimal server proof (MSC4108 works when driven manually)
# Create rendezvous
POST /_matrix/client/unstable/org.matrix.msc4108/rendezvous
Content-Type: text/plain
→ 201 Created (returns rendezvous URL + ETag)
# Simulate phone write
PUT /_synapse/client/rendezvous/
If-Match:
Content-Type: text/plain
Body: ok
→ 202 / 204
This shows Synapse + proxy are fine; the phone simply never performs the PUT after scanning.
Homeserver / proxy
Domain: matrix.example.com (Synapse behind nginx)
Synapse: 1.136.0
MAS: v1.0.0-rc.0 (OIDC present but QR flow uses Synapse rendezvous, not MAS)
MSC4108 enabled; rendezvous proxied via nginx.
Rendezvous nginx block (CORS tolerant; PUT allowed):
location ^~ /_synapse/client/rendezvous {
# Echo Origin; fallback to * if none (and adjust creds)
set $cors_origin $http_origin;
if ($cors_origin = "") { set $cors_origin "*"; }
set $cors_creds "true";
if ($cors_origin = "*") { set $cors_creds "false"; }
# Preflight
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials $cors_creds always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Vary "Origin" always;
return 204;
}
# Actual
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials $cors_creds always;
add_header Access-Control-Expose-Headers "Location, ETag" always;
add_header Vary "Origin" always;
proxy_set_header Authorization $http_authorization;
proxy_set_header Origin $http_origin; # forward if present
proxy_pass http://127.0.0.1:8008;
proxy_http_version 1.1;
proxy_read_timeout 600s;
proxy_buffering off;
}
Preflight probes return 204, Access-Control-Allow-Methods includes PUT, and manual PUT with text/plain succeeds.
Device / client
Phone: Motorola Moto Z3, Android 9 (PDX29.84-51-11-8)
Element X: 25.08.3 (installed via Aurora Store)
Chrome & Android System WebView: installed/enabled & up to date
Permissions: Camera/network enabled for Element X; Chrome/WebView enabled
Network: Reproduces on LAN and cellular
What I tried
Reinstall Element X; clear cache
Ensure Chrome + Android System WebView enabled/updated; set WebView implementation appropriately
Loosened CORS to mirror $http_origin (fallback *) and mirror requested headers; long timeouts
Verified reachability from phone: /_matrix/client/versions OK; manual rendezvous POST/PUT OK
Tried Element Android (legacy): no “Sign in with QR” on the first screen (as expected); manual login + emoji verification works
Hypothesis
On this Android 9 device, Element X’s WebView path appears to suppress the rendezvous PUT after scanning (likely CORS/origin/preflight interaction or a WebView quirk). Since manual PUT text/plain works and logs show no server-side rejects, the request seems to never leave the client.
Workaround
Manual login on phone → verify via emoji from desktop; phone becomes trusted (works).
Attachments added:
– elementx-only.log (Element X PID during QR attempt)
– Synapse log lines (GET 304 from phone+desktop → DELETE 204 by desktop → 404; no phone PUT)
– nginx rendezvous CORS block (allows PUT, mirrors Origin/headers; preflights 204)
– Verified server path works with manual POST/PUT text/plain (202/204).
I can capture full adb logcat or try a debug build if you want deeper client traces.
[elementx-only.log](https://github.com/user-attachments/files/21864241/elementx-only.log)
### Your phone model
_No response_
### Operating system version
_No response_
### Application version and app store
_No response_
### Homeserver
_No response_
### Will you send logs?
Yes
### Are you willing to provide a PR?
No
Contributor guide
Assessment
This issue has not been assessed yet.