Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC3720: Account status #3720

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
114 changes: 114 additions & 0 deletions proposals/3720-account-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# MSC3720: Account status endpoint

Matrix clients sometimes need a way to display additional information about a
user. For example, when interacting with a Matrix user, it might be useful for
clients to show whether this user's account has been deactivated, or even exists
at all.
Comment on lines +4 to +6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would a user interact with non-existent users? You can't make a room to them, I don't think. I'm failing to see when this would come up in a client!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A common use case would be trying to start a DM using an MXID: if the user is on a remote homeserver the only way to know if the MXID maps to an existing user is trying to retrieve extra data on them and see if it works, in a kinda hacky way. Currently (iirc) Element clients do this by trying to retrieve the user's profile and displaying a warning if no profile could be found, but that doesn't necessarily indicate the user doesn't exist.

Copy link
Contributor Author

@babolivier babolivier Feb 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically this is what I mean:

Peek.2022-02-16.17-45.mp4

Note that in this capture "User [...] does not exist" appears but it looks like Element added it given this is the actual response from Synapse on the profile request:

{"errcode":"M_NOT_FOUND","error":"Profile was not found"}


Currently clients are forced to resort to hacks to try to derive this
information. Some, for example, check whether a user has a profile (display
name, avatar) set when trying to send an invite, which means that if a user
doesn't have any profile information (which is valid under the Matrix
specification), the inviter might be warned that the user might not exist.

## Proposal
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved

Two new endpoints are added to the specification, one to the client-server API
and one to the server-server API.
Comment on lines +16 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an alternative to this proposal is to do one or both of the following:

  • Add error codes to the existing /profile endpoint, e.g. 404 with M_DEACTIVATED or M_DOES_NOT_EXIST.
  • Add a bulk query profile endpoint.

Was this considered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort of, except that one of the requirements I'm working with (which I haven't mentioned in the MSC) is that the response needs to be easily extensible. As mentioned in this comment, this is a port of a feature used in the Tchap infrastructure. That infrastructure uses Synapse's account validity (which isn't part of the spec and imo doesn't really have a place in there), and uses this feature to also figure out whether a user is expired (a second step of the Synapse work will be adding a config flag to Synapse to optionally include account validity status to this endpoint's response). As far as I can tell, none of the alternatives you've mentioned offer this kind of extensibility.

Copy link
Contributor Author

@babolivier babolivier Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, after giving it some more thought, it feels quite wrong to me to use the profile endpoint for information that's not profile-related.


### `GET /_matrix/client/v1/account_status`

This endpoint requires authentication via an access token.

This endpoint takes a `user_id` query parameter indicating which user account(s)
to look up the status of. This parameter may appear multiple times in the
request if the client wishes to look up the statuses of multiple users at once.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it will allow bulk username enumeration, which is usually considered a security flaw. Maybe it being authenticated is "OK" and being able to query for users on a homeserver isn't considered private info right now, but something feels off with being able to do it in bulk.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree that this can be concerning. Maybe a good compromise could be adding a limit (eg 15) to the number of users that can be looked up in a single request, and encourage homeservers to aggressively rate-limit the endpoint?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I would like the ability to not limit appservices here, as they have more of a valid need to do bulk lookups)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I would like the ability to not limit appservices here, as they have more of a valid need to do bulk lookups)

Sounds like an implementation detail?

Copy link
Contributor

@Half-Shot Half-Shot Feb 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is just an avocation for keeping bulk fetching / not setting limits in the spec but allowing implementations the leeway of setting their own limits (so as not to write-out bulk lookups in the spec or anything). No limitations in the spec covers my use case :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah fair enough :) Yeah the idea is very much to allow bulk lookups.


If no error arises, the endpoint responds with a body using the following
format:

```json
{
"account_statuses": {
"@user1:example.com": {
"exists": true,
"deactivated": false
},
"@user2:example.com": {
"exists": false
},
},
"failures": [
"@user3:example.com",
"@user4:otherexample.com"
]
}
```

The `account_statuses` object in the response lists all statuses that could
successfully be retrieved. Each key in this object maps to one of the `user_id`
parameter(s) in the request. For each account:

* `exists` is a boolean that indicates whether an account exists with this user
ID.
* `deactivated` is a boolean that indicates whether the account has been
deactivated. Omitted if `exists` is `false`.

The `failures` object in the response lists all user IDs for which no status
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is different from what /keys/claim and /keys/query do. I'm not sure if that was intentional or not. /keys/claim and /keys/query list the homeservers for which the request failed, rather individual requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional - sorry for not pointing it out explicitly. I thought that it would be good to allow homeserver implementations to refuse to serve the status of specific accounts while still providing this bit of information for other accounts. I'm happy to change this if people think it would be better to just copy what /keys/... does.

could be retrieved for any reason (e.g. federation issues, missing federation
endpoint, missing user in the remote server's response, etc).

The combination of the lists of user IDs from `user_statuses` and `failures`
must match the full list of user IDs provided in the request via `user_id`
parameters.

If one or more account(s) is not local to the homeserver this request is
performed on, the homeserver must attempt to retrieve account status using the
federation endpoint described below.

Below is how this endpoint behaves in case of errors:

* If no `user_id` parameter is provided, the endpoint responds with a 400 status
code and a `M_MISSING_PARAM` error code.
* If one or more of the `user_id` parameter(s) provided cannot be parsed as a
valid Matrix user ID, the endpoint responds with a 400 status code and a
`M_INVALID_PARAM` error code.

### `GET /_matrix/federation/v1/query/account_status`

This endpoint behaves in an identical way to the client-side endpoint described
above, with the additional following error case:

* If one or more of the `user_id` parameter(s) does not match a local account,
babolivier marked this conversation as resolved.
Show resolved Hide resolved
the endpoint responds with a 400 status code and a `M_INVALID_PARAM` error
code.

### `m.account_status` capability

Some server administrators might not want to disclose too much information about
their users. To support this use case, homeservers must expose a
`m.account_status` capability to tell clients whether they support retrieving
account status via the client-side endpoint described above.

## Security considerations

Should a server administrator not want to disclose information about their users
through the federation endpoint described above, they should use a reverse proxy
or similar tool to prevent access to the endpoint. On top of this, homeserver
implementations may implement measures to only respond with an empty JSON object
`{}` in this case.
babolivier marked this conversation as resolved.
Show resolved Hide resolved

## Unstable prefixes

Until this proposal is stabilised in a new version of the Matrix specification,
implementations should use the following paths for the endpoints described in
this document:

| Stable path | Unstable path |
|-----------------------------------------------|------------------------------------------------------------------------|
| `/_matrix/client/v1/account_status` | `/_matrix/client/unstable/org.matrix.msc3720/account_status` |
| `/_matrix/federation/v1/query/account_status` | `/_matrix/federation/unstable/org.matrix.msc3720/query/account_status` |

Additionally, implementations should use the unstable identifier
`org.matrix.msc3720.account_status` instead of `m.account_status` for the
client-side capability.