added token-authorized registration

This commit is contained in:
Givralix 2024-07-14 10:38:42 +02:00
parent 3e62b986d1
commit 618f3eaff6
5 changed files with 70 additions and 3 deletions

View file

@ -11,4 +11,5 @@ const (
LoginTypeRecaptcha = "m.login.recaptcha" LoginTypeRecaptcha = "m.login.recaptcha"
LoginTypeApplicationService = "m.login.application_service" LoginTypeApplicationService = "m.login.application_service"
LoginTypeToken = "m.login.token" LoginTypeToken = "m.login.token"
LoginTypeRegistrationToken = "m.login.registration_token"
) )

View file

@ -233,6 +233,9 @@ type authDict struct {
// Recaptcha // Recaptcha
Response string `json:"response"` Response string `json:"response"`
// Registration token
Token string `json:"token"`
// TODO: Lots of custom keys depending on the type // TODO: Lots of custom keys depending on the type
} }
@ -275,6 +278,9 @@ var (
ErrInvalidCaptcha = errors.New("invalid captcha response") ErrInvalidCaptcha = errors.New("invalid captcha response")
ErrMissingResponse = errors.New("captcha response is required") ErrMissingResponse = errors.New("captcha response is required")
ErrCaptchaDisabled = errors.New("captcha registration is disabled") ErrCaptchaDisabled = errors.New("captcha registration is disabled")
ErrRegistrationTokenDisabled = errors.New("token registration is disabled")
ErrMissingToken = errors.New("registration token is required")
ErrInvalidToken = errors.New("invalid registration token")
) )
// validateRecaptcha returns an error response if the captcha response is invalid // validateRecaptcha returns an error response if the captcha response is invalid
@ -326,6 +332,34 @@ func validateRecaptcha(
return nil return nil
} }
// validateToken returns an error response if the token is invalid
func validateToken(
req *http.Request,
userAPI userapi.ClientUserAPI,
cfg *config.ClientAPI,
token string,
) error {
if !cfg.RegistrationRequiresToken {
return ErrRegistrationTokenDisabled
}
if token == "" {
return ErrMissingToken
}
exists, err := userAPI.ValidateRegistrationToken(req.Context(), token)
if err != nil {
return err
}
if !exists {
return ErrInvalidToken
}
return nil
}
// UserIDIsWithinApplicationServiceNamespace checks to see if a given userID // UserIDIsWithinApplicationServiceNamespace checks to see if a given userID
// falls within any of the namespaces of a given Application Service. If no // falls within any of the namespaces of a given Application Service. If no
// Application Service is given, it will check to see if it matches any // Application Service is given, it will check to see if it matches any
@ -733,6 +767,25 @@ func handleRegistrationFlow(
// Add Recaptcha to the list of completed registration stages // Add Recaptcha to the list of completed registration stages
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha) sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha)
case authtypes.LoginTypeRegistrationToken:
// Check given token response
err := validateToken(req, userAPI, cfg, r.Auth.Token)
switch err {
case ErrRegistrationTokenDisabled:
return util.JSONResponse{Code: http.StatusForbidden, JSON: spec.Unknown(err.Error())}
case ErrMissingToken:
return util.JSONResponse{Code: http.StatusBadRequest, JSON: spec.BadJSON(err.Error())}
case ErrInvalidToken:
return util.JSONResponse{Code: http.StatusUnauthorized, JSON: spec.BadJSON(err.Error())}
case nil:
default:
util.GetLogger(req.Context()).WithError(err).Error("failed to validate token")
return util.JSONResponse{Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}}
}
// Add RegistrationToken to the list of completed registration stages
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypeRegistrationToken)
case authtypes.LoginTypeDummy: case authtypes.LoginTypeDummy:
// there is nothing to do // there is nothing to do
// Add Dummy to the list of completed registration stages // Add Dummy to the list of completed registration stages

View file

@ -291,6 +291,10 @@ func (config *Dendrite) Derive() error {
config.Derived.Registration.Flows = []authtypes.Flow{ config.Derived.Registration.Flows = []authtypes.Flow{
{Stages: []authtypes.LoginType{authtypes.LoginTypeRecaptcha}}, {Stages: []authtypes.LoginType{authtypes.LoginTypeRecaptcha}},
} }
} else if config.ClientAPI.RegistrationRequiresToken {
config.Derived.Registration.Flows = []authtypes.Flow{
{Stages: []authtypes.LoginType{authtypes.LoginTypeRegistrationToken}},
}
} else { } else {
config.Derived.Registration.Flows = []authtypes.Flow{ config.Derived.Registration.Flows = []authtypes.Flow{
{Stages: []authtypes.LoginType{authtypes.LoginTypeDummy}}, {Stages: []authtypes.LoginType{authtypes.LoginTypeDummy}},

View file

@ -117,6 +117,7 @@ type ClientUserAPI interface {
QueryLocalpartForThreePID(ctx context.Context, req *QueryLocalpartForThreePIDRequest, res *QueryLocalpartForThreePIDResponse) error QueryLocalpartForThreePID(ctx context.Context, req *QueryLocalpartForThreePIDRequest, res *QueryLocalpartForThreePIDResponse) error
PerformForgetThreePID(ctx context.Context, req *PerformForgetThreePIDRequest, res *struct{}) error PerformForgetThreePID(ctx context.Context, req *PerformForgetThreePIDRequest, res *struct{}) error
PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error
ValidateRegistrationToken(ctx context.Context, registrationToken string) (bool, error)
} }
type KeyBackupAPI interface { type KeyBackupAPI interface {

View file

@ -978,3 +978,11 @@ func (a *UserInternalAPI) PerformSaveThreePIDAssociation(ctx context.Context, re
} }
const pushRulesAccountDataType = "m.push_rules" const pushRulesAccountDataType = "m.push_rules"
func (a *UserInternalAPI) ValidateRegistrationToken(ctx context.Context, registrationToken string) (bool, error) {
exists, err := a.DB.RegistrationTokenExists(ctx, registrationToken)
if err != nil {
return false, err
}
return exists, nil
}