Application services assert only registered, active users, and never on Account Management¶
- Status: accepted
- Date: 2026-10-07
Technical Story: 29 September 2026 security audit, findings AUTH-7 (other services' exclusive namespaces ignored), AUTH-8 (identity assertion honoured on 3PID management) and AUTH-12 (an application service can act as a deactivated or non-existent user).
Context and Problem Statement¶
The masquerade branch of authenticated_user checked only that the asserted user ID was in the
service's own user namespace. It did not look the user up, so a service could act as a user it
had never registered, or as one an administrator had deactivated. Register, login and assertion
also ignored other services' exclusive namespaces, and assertion was honoured on Account
Management endpoints, which the spec excludes ("This applies to all aspects of the Client-Server
API, except for Account Management").
The spec says users are created through /register with m.login.application_service, but does
not say what asserting a user that was never registered should do.
Considered Options¶
- Refuse assertion of a user that is not registered or is deactivated (chosen).
- Allow unregistered users in the namespace and refuse only deactivated ones. Rejected: a service could then act as, and create rows for, users no registration ever vetted, and an administrator could not tell from the user list who a service acts as.
Decision Outcome¶
Chosen option: refuse unregistered and deactivated users, as Synapse does.
- An asserted user (explicit
user_id, or the implicitsender_localpartuser) that is not registered, or is deactivated, is refused with403 M_FORBIDDEN.POST /registerandPOST /loginare exempt: they act as the sender user, which is created at startup (AUTH-3), and check namespace ownership themselves. A bridge that asserts a puppet before registering it gets403, the signal to register first. - A service may not register, log in as, or assert a user in another service's exclusive
namespace:
/registeranswers400 M_EXCLUSIVE(the status the spec's response table gives),/loginand assertion403 M_EXCLUSIVE. An out-of-namespace/registeralso moved from 403 to 400 for the same reason. - Every path under
/_matrix/client/<version>/accountrefuses a masqueraded request with403 M_FORBIDDEN, exceptGET /account/whoami, which the spec's own identity-assertion example uses. An unknown future/account/...path is refused too (fail closed). - The checks run under the request lock in
handle_client_server_request, andauthenticated_userrepeats the namespace, exclusivity and active checks as defence in depth.
Negative Consequences¶
- Bridges that relied on asserting unregistered puppets must register them first.
- Device management (
/devices,/delete_devices) still honours assertion; bridges usedevice_idmasquerading, and the spec's Account Management section does not list them as endpoints.