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

MSC3744: Support for flexible authentication #3744

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
273 changes: 273 additions & 0 deletions proposals/3744-flexible-auth.md
@@ -0,0 +1,273 @@
# MSCxxxx: Support for Flexible Authentication

In the Matrix client-server API, the `/login` endpoint allows the client
to specify which authentication mechanism should be used to log the user
in, e.g. `"type": "m.login.password"`.
However, the CS API lacks a way to set up other authenticators for the
user beyond a simple password.

This proposal describes changes to the CS API to enable clients to
configure other authentication methods, for both new and existing accounts.
The changes proposed here would be required for improving the security of
Matrix's login in various ways, for example by using cryptographic password
authentication protocols
([MSC3726](https://github.com/matrix-org/matrix-doc/pull/3726))
or by adding support two-factor authentication (e.g.
[MSC1998](https://github.com/matrix-org/matrix-doc/pull/1998))
to be used alongside the traditional `m.login.password`.

## Proposal

### No change to `POST /login`
The `/login` API endpoint already supports authentication types beyond just
`m.login.password`.

### Changes to `POST /register`

Instead of sending the user's password as a top-level element of the JSON body,
the client should now provide a dictionary of authenticators for the login type(s)
that the user wishes to use.
The keys of the dictionary are authentication types, e.g. `m.login.password`,
and the values are the authenticator data.
The contents of the authenticator data will vary from one authentication
type to the next.

For example, to register an account that will use traditional password login,
the JSON body from the CS API documentation becomes:

```json
{
"auth": {
"example_credential": "verypoorsharedsecret",
"session": "xxxxx",
"type": "example.type.foo"
},
"device_id": "GHTYAJCE",
"initial_device_display_name": "Jungle Phone",
"username": "cheeky_monkey"
"authenticators": {
"m.login.password": {
"password": "ilovebananas",
}
}
}
```

As another example, consider the BS-SPEKE protocol in [MSC3726](https://github.com/matrix-org/matrix-doc/pull/3726).
There, the server needs to know the user's base elliptic curve point
`P`, their public key `V`, and their parameters for the password hashing
function (PHF).
To register a user with only BS-SPEKE authentication, the JSON body might be:

```json
{
"auth": {
"example_credential": "verypoorsharedsecret",
"session": "xxxxx",
"type": "example.type.foo"
},
"device_id": "GHTYAJCE",
"initial_device_display_name": "Jungle Phone",
"username": "cheeky_monkey"
"authenticators": {
"m.login.bsspeke.ecc": {
"P": "<user's base64-encoded curve25519 base point>",
"V": "<user's base64-encoded long-term curve25519 public key>",
"phf_params": {
"name": "argon2i",
"blocks": 100000,
"iterations": 3,
},
}
}
}
```

### New `GET /register` endpoint
The client also needs some way to know which authenticators are supported
by the server.
The proposed approach is to add a new `GET` method for `/register`, similar
to what is already done for `/login`.
The server responds with a list of the supported authentication types.

```json
{
"auth_types": [
"m.login.password",
]
}
```


### Deprecation of the old `/account/password` endpoint

Under this proposal, the `/account/password` endpoint would be deprecated
in favor of the new, more general `account/authenticator` endpoint described
below.

### New `POST /account/authenticator` endpoint

This replaces the old `/account/password` endpoint with a more flexible
version that can handle more different types of authenticators.

The request body should contain a dictionary of authenticators, where the
keys are authentication types, e.g. `m.login.password`, and the values
are the authenticator data.
The contents of the authenticator data will vary from one authentication
type to the next.

For example, for traditional password authentication:

`POST /account/authenticator`

```json
{
"m.login.password": {
"password": "<user's login password>"
},

}
```

As another example, consider the BS-SPEKE protocol in MSC3726.
There, the server needs to know the user's base elliptic curve point
`P`, their public key `V`, and their parameters for the password hashing
function (PHF).
Setting up a user to log in with BS-SPEKE might look like this:

`POST /account/authenticator`

```json
{
"m.login.bsspeke.ecc": {
"P": "<user's base64-encoded curve25519 base point>",
"V": "<user's base64-encoded long-term curve25519 public key>",
"phf_params": {
"name": "argon2i",
"blocks": 100000,
"iterations": 3,
},
},
}
```

**NOTE:** Just like the old `/account/password` endpoint, the new
`/account/authenticator` endpoint requires user-interactive authentication.
Cryptographic authentication mechanisms, such as BS-SPEKE or Webauthn,
typically require multiple rounds of communication between client and
server in order to set up a new authenticator for the user.
These protocols can use a UIA stage to implement the initial round(s)
of their setup ceremony.
Servers that wish to support these protocols then MUST include those
UIA stages in the UIA flows for this endpoint.

### New `DELETE /account/authenticator/<auth_type>/<authenticator_id>` endpoint

To un-register an authenticator, the client calls

```
DELETE /account/authenticator/auth_type
```

For example,

```
DELETE /account/authenticator/m.login.password
```

Some authentication mechanisms allow the user to configure multiple
authenticators of the same type.
For example, with WebAuthn, a user might have two FIDO2 keys, Apple
TouchID, and Apple FaceID.
Each of these authenticators should have a unique identifier.
If the `auth_type` for WebAuthn is `m.login.webauthn`, and one of the
WebAuthn authenticators has id `abcdwxyz`, then the client can remove
the authenticator, leaving all other WebAuthn authenticators intact,
by calling

```
DELETE /account/authenticator/m.login.webauthn/abcdwxyz
```





## Potential issues

**Backwards compatibility.**
For the immediate future, servers should continue to accept the old style requests.

* For `/register`, if the request's JSON body does not include an
`authenticators` object, the server should look for an old style
`password`.

* Requests for `POST /account/password` should be treated as an alias
for `POST /account/authenticator` with a type of `m.login.password`.


## Alternatives

**OpenID Connect.**
Rather than implementing more authentication types in the Matrix CS
API, Matrix could switch to using OpenID Connect.
Then all the complexity of handling different authentication mechanisms
is pushed out into the OIDC Provider.

OIDC might be the proper direction to go in the long run.
But for now, as a stop-gap measure, we can build better security using
the building blocks that we have in the existing CS API.
Also, some installations may not want to depend on a third party OIDC
Provider for a foundational thing like authentication; this proposal
provides a way for such homeservers to achieve better security with no
third parties.
Comment on lines +211 to +223
Copy link
Member

Choose a reason for hiding this comment

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

+1 to OIDC; the Matrix project see this as this as the correct direction (see matrix-org/matrix-spec#636).

I understand your desire to achieve improvements in a shorter timescale than OIDC can happen, but... given limited resources, I'm hesitant to spend much time on changes that we essentially plan to throw away in the next few months.

Copy link
Author

Choose a reason for hiding this comment

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

I talked to Quentin about OIDC last week. It's very exciting stuff, and good to see Matrix building out capabilities in that direction.

However, IMO making OIDC the only supported option is premature at best, and it's an extreme measure for an open source project to be taking. Frankly it's a weird thing for a consumer chat app to be doing -- imagine DIscord or Signal or Telegram forcing you to hop through a web page to log in.

My own project requires a non-OIDC solution, so that's what I'm building. I'm trying to do it in a way that can be most useful to the rest of the community.

Copy link

@Xnsamtr Xnsamtr Mar 30, 2022

Choose a reason for hiding this comment

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

How would OIDC work with something like Dendrite+Pinecone? Without forcing individual users to auth with centralized server(s) and without making the setup considerably more complicated for them than it would need to be?
It seems problematic for the case of making a client that connects over Tor or similar networks, too.

Copy link

Choose a reason for hiding this comment

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

There seems to be some assumption that federated homeservers in the foreseeable will look much like the current model, with the addition of p2p meaning either just that, or single-user-local-device where auth is either native or non-existant.

@Xnsamtr notes some ways that these assumptions don't necessarily hold. OIDC covers a lot of use-cases (possibly even the majority of users over the coming years) but opening up for alternatives is A Good Thing. I'd even speculate it'll become a necessity for Matrix to fulfill its goals. Identifying least-common denominators (which I see this as a good attempt at) means that people like @cvwright can solve their problems while being as compatible as they can with other implementations.


## Security considerations
1. Two-factor authentication is quickly becoming a baseline requirement
for many organizations.
This proposal provides an important step toward enabling two-factor auth
in Matrix, without the need for integrating with any external system.

2. Any proposal for authenticating users with a cryptographic protocol
will need some way for the client to register the user's authenticators
for that protocol on the server.
This proposal provides that mechanism.

3. The approach proposed here would allow clients to configure multiple
password-based authentication mechanisms for a single user, for example,
`m.login.password` and a cryptographic password authentication protocol
such as MSC3726.
If the user sets the same password for both authentication mechanisms,
then much of the security benefit of the cryptographic login is lost.

* Clients should not automatically register both types of authenticators
from a single password, and they should take some care to detect when
a user chooses to set the same password for both types of auth.

* Note that this is not *always* bad.
For example, if the user started with the legacy `m.login.password` auth,
then it is probably better to allow them to upgrade to a more secure
auth scheme, even if they don't make the optimal choice of also setting
a new password when they upgrade.

* On the other hand, if a user is already set up for cryptographic login,
and they attempt to set up `m.login.password` using the same password,
then the client should do its best to alert them of the risk.
Perhaps the downgrade functionality should be hidden behind some sort
of "advanced options", and/or disabled by default.



## Potential issues

???



## Unstable prefix

TBD

## Dependencies

None