Takwaiw/dendrite publickey (#2)

* Implementation of MSC 3782 Add publickey login as a new auth type.

Co-authored-by: Tak Wai Wong <takwaiw@gmail.com>
This commit is contained in:
Tak Wai Wong 2022-05-12 16:47:48 -07:00
parent 56194c4e32
commit 3145a9dc52
No known key found for this signature in database
GPG key ID: 222E4AF2AA1F467D
6 changed files with 52 additions and 14 deletions

View file

@ -246,7 +246,7 @@ func (u *UserInteractive) ResponseWithChallenge(sessionID string, response inter
// Verify returns an error/challenge response to send to the client, or nil if the user is authenticated. // Verify returns an error/challenge response to send to the client, or nil if the user is authenticated.
// `bodyBytes` is the HTTP request body which must contain an `auth` key. // `bodyBytes` is the HTTP request body which must contain an `auth` key.
// Returns the login that was verified for additional checks if required. // Returns the login that was verified for additional checks if required.
func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *api.Device) (*Login, *util.JSONResponse) { func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte) (*Login, *util.JSONResponse) {
// TODO: rate limit // TODO: rate limit
// "A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body" // "A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body"

View file

@ -17,11 +17,6 @@ var (
serverName = gomatrixserverlib.ServerName("example.com") serverName = gomatrixserverlib.ServerName("example.com")
// space separated localpart+password -> account // space separated localpart+password -> account
lookup = make(map[string]*api.Account) lookup = make(map[string]*api.Account)
device = &api.Device{
AccessToken: "flibble",
DisplayName: "My Device",
ID: "device_id_goes_here",
}
) )
type fakeAccountDatabase struct { type fakeAccountDatabase struct {
@ -60,7 +55,7 @@ func setup() *UserInteractive {
func TestUserInteractiveChallenge(t *testing.T) { func TestUserInteractiveChallenge(t *testing.T) {
uia := setup() uia := setup()
// no auth key results in a challenge // no auth key results in a challenge
_, errRes := uia.Verify(ctx, []byte(`{}`), device) _, errRes := uia.Verify(ctx, []byte(`{}`))
if errRes == nil { if errRes == nil {
t.Fatalf("Verify succeeded with {} but expected failure") t.Fatalf("Verify succeeded with {} but expected failure")
} }
@ -100,7 +95,7 @@ func TestUserInteractivePasswordLogin(t *testing.T) {
}`), }`),
} }
for _, tc := range testCases { for _, tc := range testCases {
_, errRes := uia.Verify(ctx, tc, device) _, errRes := uia.Verify(ctx, tc)
if errRes != nil { if errRes != nil {
t.Errorf("Verify failed but expected success for request: %s - got %+v", string(tc), errRes) t.Errorf("Verify failed but expected success for request: %s - got %+v", string(tc), errRes)
} }
@ -181,7 +176,7 @@ func TestUserInteractivePasswordBadLogin(t *testing.T) {
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
_, errRes := uia.Verify(ctx, tc.body, device) _, errRes := uia.Verify(ctx, tc.body)
if errRes == nil { if errRes == nil {
t.Errorf("Verify succeeded but expected failure for request: %s", string(tc.body)) t.Errorf("Verify succeeded but expected failure for request: %s", string(tc.body))
continue continue

View file

@ -28,7 +28,7 @@ func Deactivate(
} }
} }
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, deviceAPI) login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
if errRes != nil { if errRes != nil {
return *errRes return *errRes
} }

View file

@ -198,7 +198,7 @@ func DeleteDeviceById(
sessionID = s sessionID = s
} }
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, device) login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
if errRes != nil { if errRes != nil {
switch data := errRes.JSON.(type) { switch data := errRes.JSON.(type) {
case auth.Challenge: case auth.Challenge:

View file

@ -3,6 +3,8 @@ package config
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
) )
type ClientAPI struct { type ClientAPI struct {
@ -160,3 +162,44 @@ func (r *RateLimiting) Defaults() {
r.Threshold = 5 r.Threshold = 5
r.CooloffMS = 500 r.CooloffMS = 500
} }
type ethereumAuthParams struct {
Version uint32 `json:"version"`
ChainIDs []string `json:"chain_ids"`
}
type ethereumAuthConfig struct {
Enabled bool `yaml:"enabled"`
Version uint32 `yaml:"version"`
ChainIDs []string `yaml:"chain_ids"`
}
type publicKeyAuthentication struct {
Ethereum ethereumAuthConfig `yaml:"ethereum"`
}
func (pk *publicKeyAuthentication) Enabled() bool {
return pk.Ethereum.Enabled
}
func (pk *publicKeyAuthentication) GetPublicKeyRegistrationFlows() []authtypes.Flow {
var flows []authtypes.Flow
if pk.Ethereum.Enabled {
flows = append(flows, authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypePublicKeyEthereum}})
}
return flows
}
func (pk *publicKeyAuthentication) GetPublicKeyRegistrationParams() map[string]interface{} {
params := make(map[string]interface{})
if pk.Ethereum.Enabled {
p := ethereumAuthParams{
Version: pk.Ethereum.Version,
ChainIDs: pk.Ethereum.ChainIDs,
}
params[authtypes.LoginTypePublicKeyEthereum] = p
}
return params
}

View file

@ -300,10 +300,10 @@ func Test_UserStatistics(t *testing.T) {
}, },
R30UsersV2: map[string]int64{ R30UsersV2: map[string]int64{
"ios": 0, "ios": 0,
"android": 0, "android": 1,
"web": 0, "web": 1,
"electron": 0, "electron": 0,
"all": 0, "all": 2,
}, },
AllUsers: 6, AllUsers: 6,
NonBridgedUsers: 5, NonBridgedUsers: 5,