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)
or by adding support two-factor authentication (e.g.
MSC1998)
to be used alongside the traditional m.login.password
.
The /login
API endpoint already supports authentication types beyond just
m.login.password
.
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:
{
"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.
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:
{
"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,
},
}
}
}
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.
{
"auth_types": [
"m.login.password",
]
}
Under this proposal, the /account/password
endpoint would be deprecated
in favor of the new, more general account/authenticator
endpoint described
below.
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
{
"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
{
"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.
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
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 anauthenticators
object, the server should look for an old stylepassword
. -
Requests for
POST /account/password
should be treated as an alias forPOST /account/authenticator
with a type ofm.login.password
.
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.
-
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.
-
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.
-
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.
???
TBD
None