mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-16 18:43:10 -06:00
pull latest from dendrite-fork subtree
This commit is contained in:
parent
88bbaed247
commit
34342934cc
|
|
@ -16,20 +16,14 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/spruceid/siwe-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginPublicKeyEthereum struct {
|
type LoginPublicKeyEthereum struct {
|
||||||
|
|
@ -39,28 +33,11 @@ type LoginPublicKeyEthereum struct {
|
||||||
Session string `json:"session"`
|
Session string `json:"session"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Signature string `json:"signature"`
|
Signature string `json:"signature"`
|
||||||
HashFields publicKeyEthereumHashFields `json:"hashFields"`
|
|
||||||
HashFieldsRaw string // Raw base64 encoded string of MessageFields for hash verification
|
|
||||||
|
|
||||||
userAPI userapi.ClientUserAPI
|
userAPI userapi.ClientUserAPI
|
||||||
config *config.ClientAPI
|
config *config.ClientAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
type publicKeyEthereumHashFields struct {
|
|
||||||
// Todo: See https://...
|
|
||||||
Domain string `json:"domain"` // home server domain
|
|
||||||
Address string `json:"address"` // Ethereum address. 0x...
|
|
||||||
Nonce string `json:"nonce"` // session ID
|
|
||||||
Version string `json:"version"` // version of the Matrix public key spec that the client is complying with
|
|
||||||
ChainId string `json:"chainId"` // blockchain network ID.
|
|
||||||
}
|
|
||||||
|
|
||||||
type publicKeyEthereumRequiredFields struct {
|
|
||||||
From string // Sender
|
|
||||||
To string // Recipient
|
|
||||||
Hash string // Hash of JSON representation of the message fields
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreatePublicKeyEthereumHandler(
|
func CreatePublicKeyEthereumHandler(
|
||||||
reqBytes []byte,
|
reqBytes []byte,
|
||||||
userAPI userapi.ClientUserAPI,
|
userAPI userapi.ClientUserAPI,
|
||||||
|
|
@ -71,15 +48,8 @@ func CreatePublicKeyEthereumHandler(
|
||||||
return nil, jsonerror.BadJSON("auth")
|
return nil, jsonerror.BadJSON("auth")
|
||||||
}
|
}
|
||||||
|
|
||||||
hashFields := gjson.GetBytes(reqBytes, "hashFields")
|
|
||||||
if !hashFields.Exists() {
|
|
||||||
return nil, jsonerror.BadJSON("auth.hashFields")
|
|
||||||
}
|
|
||||||
|
|
||||||
pk.config = config
|
pk.config = config
|
||||||
pk.userAPI = userAPI
|
pk.userAPI = userAPI
|
||||||
// Save raw bytes for hash verification later.
|
|
||||||
pk.HashFieldsRaw = hashFields.Raw
|
|
||||||
// Case-insensitive
|
// Case-insensitive
|
||||||
pk.Address = strings.ToLower(pk.Address)
|
pk.Address = strings.ToLower(pk.Address)
|
||||||
|
|
||||||
|
|
@ -116,41 +86,20 @@ func (pk LoginPublicKeyEthereum) AccountExists(ctx context.Context) (string, *js
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.MatrixError) {
|
func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.MatrixError) {
|
||||||
// Check signature to verify message was not tempered
|
// Parse the message to extract all the fields.
|
||||||
isVerified := verifySignature(pk.Address, []byte(pk.Message), pk.Signature)
|
message, err := siwe.ParseMessage(pk.Message)
|
||||||
if !isVerified {
|
|
||||||
return false, jsonerror.InvalidSignature("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the required message fields for validation
|
|
||||||
requiredFields, err := extractRequiredMessageFields(pk.Message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, jsonerror.MissingParam("message does not contain domain, address, or hash")
|
return false, jsonerror.InvalidParam("auth.message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the hash is valid for the message fields.
|
// Check signature to verify message was not tempered
|
||||||
if !verifyHash(pk.HashFieldsRaw, requiredFields.Hash) {
|
_, err = message.Verify(pk.Signature, (*string)(&pk.config.Matrix.ServerName), nil, nil)
|
||||||
return false, jsonerror.Forbidden("error verifying message hash")
|
if err != nil {
|
||||||
}
|
return false, jsonerror.InvalidSignature(err.Error())
|
||||||
|
|
||||||
// Unmarshal the hashFields for further validation
|
|
||||||
var authData publicKeyEthereumHashFields
|
|
||||||
if err := json.Unmarshal([]byte(pk.HashFieldsRaw), &authData); err != nil {
|
|
||||||
return false, jsonerror.BadJSON("auth.hashFields")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error if the message is not from the expected public address
|
|
||||||
if pk.Address != requiredFields.From || requiredFields.From != pk.HashFields.Address {
|
|
||||||
return false, jsonerror.Forbidden("address")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error if the message is not for the home server
|
|
||||||
if requiredFields.To != pk.HashFields.Domain {
|
|
||||||
return false, jsonerror.Forbidden("domain")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error if the chainId is not supported by the server.
|
// Error if the chainId is not supported by the server.
|
||||||
if !contains(pk.config.PublicKeyAuthentication.Ethereum.ChainIDs, authData.ChainId) {
|
if !contains(pk.config.PublicKeyAuthentication.Ethereum.ChainIDs, message.GetChainID()) {
|
||||||
return false, jsonerror.Forbidden("chainId")
|
return false, jsonerror.Forbidden("chainId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,75 +118,7 @@ func (pk LoginPublicKeyEthereum) CreateLogin() *Login {
|
||||||
return &login
|
return &login
|
||||||
}
|
}
|
||||||
|
|
||||||
// The required fields in the signed message are:
|
func contains(list []int, element int) bool {
|
||||||
// 1. Domain -- home server. First non-whitespace characters in the first line.
|
|
||||||
// 2. Address -- public address of the user. Starts with 0x... in the second line on its own.
|
|
||||||
// 3. Hash -- Base64-encoded hash string of the metadata that represents the message.
|
|
||||||
// The rest of the fields are informational, and will be used in the future.
|
|
||||||
var regexpAuthority = regexp.MustCompile(`^\S+`)
|
|
||||||
var regexpAddress = regexp.MustCompile(`\n(?P<address>0x\w+)\n`)
|
|
||||||
var regexpHash = regexp.MustCompile(`\nHash: (?P<hash>.*)\n`)
|
|
||||||
|
|
||||||
func extractRequiredMessageFields(message string) (*publicKeyEthereumRequiredFields, error) {
|
|
||||||
var requiredFields publicKeyEthereumRequiredFields
|
|
||||||
/*
|
|
||||||
service.org wants you to sign in with your account:
|
|
||||||
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
|
|
||||||
|
|
||||||
I accept the ServiceOrg Terms of Service: https://service.org/tos
|
|
||||||
|
|
||||||
Hash: yfSIwarByPfKFxeYSCWN3XoIgNgeEFJffbwFA+JxYbA=
|
|
||||||
*/
|
|
||||||
|
|
||||||
requiredFields.To = regexpAuthority.FindString(message)
|
|
||||||
|
|
||||||
from := regexpAddress.FindStringSubmatch(message)
|
|
||||||
if len(from) == 2 {
|
|
||||||
requiredFields.From = from[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := regexpHash.FindStringSubmatch(message)
|
|
||||||
if len(hash) == 2 {
|
|
||||||
requiredFields.Hash = hash[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requiredFields.To) == 0 || len(requiredFields.From) == 0 || len(requiredFields.Hash) == 0 {
|
|
||||||
return nil, errors.New("required message fields are missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make these fields case-insensitive
|
|
||||||
requiredFields.From = strings.ToLower(requiredFields.From)
|
|
||||||
requiredFields.To = strings.ToLower(requiredFields.To)
|
|
||||||
|
|
||||||
return &requiredFields, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifySignature(from string, message []byte, signature string) bool {
|
|
||||||
decodedSig := hexutil.MustDecode(signature)
|
|
||||||
|
|
||||||
message = accounts.TextHash(message)
|
|
||||||
// Issue: https://stackoverflow.com/questions/49085737/geth-ecrecover-invalid-signature-recovery-id
|
|
||||||
// Fix: https://gist.github.com/dcb9/385631846097e1f59e3cba3b1d42f3ed#file-eth_sign_verify-go
|
|
||||||
decodedSig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
|
|
||||||
|
|
||||||
recovered, err := crypto.SigToPub(message, decodedSig)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
recoveredAddr := crypto.PubkeyToAddress(*recovered)
|
|
||||||
|
|
||||||
addressStr := strings.ToLower(recoveredAddr.Hex())
|
|
||||||
return from == addressStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyHash(rawStr string, expectedHash string) bool {
|
|
||||||
hash := crypto.Keccak256([]byte(rawStr))
|
|
||||||
hashStr := base64.StdEncoding.EncodeToString(hash)
|
|
||||||
return expectedHash == hashStr
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(list []string, element string) bool {
|
|
||||||
for _, i := range list {
|
for _, i := range list {
|
||||||
if i == element {
|
if i == element {
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ func TestLoginFromJSONReader(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
userInteractive := UserInteractive{
|
userInteractive := UserInteractive{
|
||||||
Completed: []string{},
|
|
||||||
Flows: []userInteractiveFlow{},
|
Flows: []userInteractiveFlow{},
|
||||||
Types: make(map[string]Type),
|
Types: make(map[string]Type),
|
||||||
Sessions: make(map[string][]string),
|
Sessions: make(map[string][]string),
|
||||||
|
|
@ -148,7 +147,6 @@ func TestBadLoginFromJSONReader(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
userInteractive := UserInteractive{
|
userInteractive := UserInteractive{
|
||||||
Completed: []string{},
|
|
||||||
Flows: []userInteractiveFlow{},
|
Flows: []userInteractiveFlow{},
|
||||||
Types: make(map[string]Type),
|
Types: make(map[string]Type),
|
||||||
Sessions: make(map[string][]string),
|
Sessions: make(map[string][]string),
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/internal/mapsutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/userapi/api"
|
"github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
|
@ -177,17 +178,27 @@ type Challenge struct {
|
||||||
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
// Challenge returns an HTTP 401 with the supported flows for authenticating
|
||||||
func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse {
|
func (u *UserInteractive) challenge(sessionID string) *util.JSONResponse {
|
||||||
u.RLock()
|
u.RLock()
|
||||||
|
paramsCopy := mapsutil.MapCopy(u.Params)
|
||||||
completed := u.Sessions[sessionID]
|
completed := u.Sessions[sessionID]
|
||||||
flows := u.Flows
|
flows := u.Flows
|
||||||
u.RUnlock()
|
u.RUnlock()
|
||||||
|
|
||||||
|
for key, element := range paramsCopy {
|
||||||
|
p := GetAuthParams(element)
|
||||||
|
if p != nil {
|
||||||
|
// If an auth flow has params,
|
||||||
|
// send it as part of the challenge.
|
||||||
|
paramsCopy[key] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: 401,
|
Code: 401,
|
||||||
JSON: Challenge{
|
JSON: Challenge{
|
||||||
Completed: completed,
|
Completed: completed,
|
||||||
Flows: flows,
|
Flows: flows,
|
||||||
Session: sessionID,
|
Session: sessionID,
|
||||||
Params: u.Params,
|
Params: paramsCopy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +244,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) (*Login, *util.JSONResponse) {
|
func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *api.Device) (*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"
|
||||||
|
|
@ -284,3 +295,12 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte) (*Login,
|
||||||
// TODO: Check if there's more stages to go and return an error
|
// TODO: Check if there's more stages to go and return an error
|
||||||
return login, nil
|
return login, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAuthParams(params interface{}) interface{} {
|
||||||
|
v, ok := params.(config.AuthParams)
|
||||||
|
if ok {
|
||||||
|
p := v.GetParams()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ 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 {
|
||||||
|
|
@ -55,7 +60,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(`{}`))
|
_, errRes := uia.Verify(ctx, []byte(`{}`), device)
|
||||||
if errRes == nil {
|
if errRes == nil {
|
||||||
t.Fatalf("Verify succeeded with {} but expected failure")
|
t.Fatalf("Verify succeeded with {} but expected failure")
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +100,7 @@ func TestUserInteractivePasswordLogin(t *testing.T) {
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
_, errRes := uia.Verify(ctx, tc)
|
_, errRes := uia.Verify(ctx, tc, device)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +181,7 @@ func TestUserInteractivePasswordBadLogin(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
_, errRes := uia.Verify(ctx, tc.body)
|
_, errRes := uia.Verify(ctx, tc.body, device)
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func Deactivate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
|
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, deviceAPI)
|
||||||
if errRes != nil {
|
if errRes != nil {
|
||||||
return *errRes
|
return *errRes
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ func DeleteDeviceById(
|
||||||
sessionID = s
|
sessionID = s
|
||||||
}
|
}
|
||||||
|
|
||||||
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes)
|
login, errRes := userInteractiveAuth.Verify(ctx, bodyBytes, device)
|
||||||
if errRes != nil {
|
if errRes != nil {
|
||||||
switch data := errRes.JSON.(type) {
|
switch data := errRes.JSON.(type) {
|
||||||
case auth.Challenge:
|
case auth.Challenge:
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,13 @@ func handlePublicKeyRegistration(
|
||||||
return false, "", nil
|
return false, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := sessions.sessions[authHandler.GetSession()]; !ok {
|
||||||
|
return false, "", &util.JSONResponse{
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
JSON: jsonerror.Unknown("the session ID is missing or unknown."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isCompleted, jerr := authHandler.ValidateLoginResponse()
|
isCompleted, jerr := authHandler.ValidateLoginResponse()
|
||||||
if jerr != nil {
|
if jerr != nil {
|
||||||
return false, "", &util.JSONResponse{
|
return false, "", &util.JSONResponse{
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,16 @@ client_api:
|
||||||
# of whether registration is otherwise disabled.
|
# of whether registration is otherwise disabled.
|
||||||
registration_shared_secret: ""
|
registration_shared_secret: ""
|
||||||
|
|
||||||
|
# Disable password authentication.
|
||||||
|
password_authentication_disabled: false
|
||||||
|
|
||||||
|
# public key authentication
|
||||||
|
public_key_authentication:
|
||||||
|
ethereum:
|
||||||
|
enabled: false
|
||||||
|
version: 1
|
||||||
|
chain_ids: []
|
||||||
|
|
||||||
# Whether to require reCAPTCHA for registration. If you have enabled registration
|
# Whether to require reCAPTCHA for registration. If you have enabled registration
|
||||||
# then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used
|
# then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used
|
||||||
# for coordinated spam attacks.
|
# for coordinated spam attacks.
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,18 @@ func MapsUnion(a map[string]interface{}, b map[string]interface{}) map[string]in
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make a copy of the map
|
||||||
|
func MapCopy(m map[string]interface{}) map[string]interface{} {
|
||||||
|
cp := make(map[string]interface{})
|
||||||
|
for k, v := range m {
|
||||||
|
vm, ok := v.(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
cp[k] = MapCopy(vm)
|
||||||
|
} else {
|
||||||
|
cp[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ func (config *Dendrite) Derive() error {
|
||||||
config.Derived.Registration.Params[authtypes.LoginTypeRecaptcha] = map[string]string{"public_key": config.ClientAPI.RecaptchaPublicKey}
|
config.Derived.Registration.Params[authtypes.LoginTypeRecaptcha] = map[string]string{"public_key": config.ClientAPI.RecaptchaPublicKey}
|
||||||
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
||||||
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeRecaptcha}})
|
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeRecaptcha}})
|
||||||
} else {
|
} else if !config.ClientAPI.PasswordAuthenticationDisabled {
|
||||||
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
config.Derived.Registration.Flows = append(config.Derived.Registration.Flows,
|
||||||
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeDummy}})
|
authtypes.Flow{Stages: []authtypes.LoginType{authtypes.LoginTypeDummy}})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ package config
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientAPI struct {
|
type ClientAPI struct {
|
||||||
|
|
@ -162,44 +160,3 @@ 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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
83
setup/config/config_publickey.go
Normal file
83
setup/config/config_publickey.go
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nonceLength = 32
|
||||||
|
|
||||||
|
type AuthParams interface {
|
||||||
|
GetParams() interface{}
|
||||||
|
GetNonce() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EthereumAuthParams struct {
|
||||||
|
Version uint `json:"version"`
|
||||||
|
ChainIDs []int `json:"chain_ids"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p EthereumAuthParams) GetParams() interface{} {
|
||||||
|
copyP := p
|
||||||
|
copyP.ChainIDs = make([]int, len(p.ChainIDs))
|
||||||
|
copy(copyP.ChainIDs, p.ChainIDs)
|
||||||
|
copyP.Nonce = newNonce(nonceLength)
|
||||||
|
return copyP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p EthereumAuthParams) GetNonce() string {
|
||||||
|
return p.Nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
type ethereumAuthConfig struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Version uint `yaml:"version"`
|
||||||
|
ChainIDs []int `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,
|
||||||
|
Nonce: "",
|
||||||
|
}
|
||||||
|
params[authtypes.LoginTypePublicKeyEthereum] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
const lettersAndNumbers = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
func newNonce(n int) string {
|
||||||
|
nonce := make([]byte, n)
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
for i := range nonce {
|
||||||
|
nonce[i] = lettersAndNumbers[rand.Int63()%int64(len(lettersAndNumbers))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(nonce)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue