Description
Transaction IDs are currently scoped by access token. They are used to make some request idempotent, and to help client map sent event when they get back in /sync
.
Problem is, since MSC2918 (aka. refresh tokens), a client may refresh its access token, which may lead to scenarios like this:
- the client starts a
/sync
with an access token T1 - since T1 is about to expire, it refreshes it and gets an access token T2
- it sends an event via
/rooms/{room}/send/{type}/{txnId}
with the access token T2 /sync
gets back with the newly created event, but without thetransaction_id
field, since that/sync
was done with another access token
which directly contradicts the spec, where says on the transaction_id
field:
The client-supplied transaction ID, for example, provided via
PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}
, if the client being given the event is the same one which sent it.
There are other places in the spec where we mention those transaction IDs:
The client-server API typically uses
HTTP PUT
to submit requests with a client-generated transaction identifier. This means that these requests are idempotent. The scope of a transaction identifier is a particular access token. It only serves to identify new requests from retransmits. After the request has finished, the {txnId} value should be changed (how is not specified; a monotonically increasing integer is recommended).
And on the txnId
on /send
:
The transaction ID for this event. Clients should generate an ID unique across requests with the same access token; it will be used by the server to ensure idempotency of requests.
What I suggest is to use the device_id
(+ user_id
) instead of the access token to scope those transaction IDs.
Related Synapse issue: matrix-org/synapse#13064 and PR: matrix-org/synapse#13083