Support for more flexible authentication
parent
c420fc49a0
commit
41e4c6b811
@ -0,0 +1,269 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
### 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 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.
|
||||||
|
|
||||||
|
`GET /register`
|
||||||
|
|
||||||
|
```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 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>` 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
Loading…
Reference in New Issue