diff --git a/clientapi/auth/authtypes/logintypes.go b/clientapi/auth/authtypes/logintypes.go index da0324251..b93f545c5 100644 --- a/clientapi/auth/authtypes/logintypes.go +++ b/clientapi/auth/authtypes/logintypes.go @@ -10,4 +10,5 @@ const ( LoginTypeSharedSecret = "org.matrix.login.shared_secret" LoginTypeRecaptcha = "m.login.recaptcha" LoginTypeApplicationService = "m.login.application_service" + LoginTypeEmailIdentity = "m.login.email.identity" ) diff --git a/clientapi/routing/register.go b/clientapi/routing/register.go index 872bdd736..cd5dd6502 100644 --- a/clientapi/routing/register.go +++ b/clientapi/routing/register.go @@ -150,9 +150,34 @@ type authDict struct { // Recaptcha Response string `json:"response"` + // m.login.email.identity and m.login.msisdn + ThreePidCreds *threepidCreds `json:"threepidCreds"` // TODO: Lots of custom keys depending on the type } +type threepidCreds struct { + Sid string `json:"sid"` + ClientSecret string `json:"client_secret"` + IdServer string `json:"id_server"` + IdAccessToken string `json:"id_access_token"` +} + +func (c *threepidCreds) validate() *jsonerror.MatrixError { + if c.Sid == "" { + return jsonerror.BadJSON("sid field in threepidCreds is required") + } + if c.ClientSecret == "" { + return jsonerror.BadJSON("client_secret in threepidCreds is required") + } + if c.IdServer == "" { + return jsonerror.BadJSON("id_server in threepidCreds is required") + } + if c.IdAccessToken == "" { + return jsonerror.BadJSON("id_access_token in threepidCreds is required") + } + return nil +} + // http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#user-interactive-authentication-api type userInteractiveResponse struct { Flows []authtypes.Flow `json:"flows"` @@ -310,6 +335,55 @@ func validateRecaptcha( return nil } +func validateEmailIdentity( + ctx context.Context, + cred *threepidCreds, + // cfg *config.ClientAPI, +) *util.JSONResponse { + url := strings.Join([]string{ + cred.IdServer, + "_matrix/identity/api/v1/3pid/getValidated3pid", + }, "/") + req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + if err != nil { + return &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.InternalServerError(), + } + } + q := req.URL.Query() + q.Add("client_secret", cred.ClientSecret) + q.Add("sid", cred.Sid) + req.URL.RawQuery = q.Encode() + resp, err := http.DefaultClient.Do(req) + if err != nil { + return &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("validate 3pid on indentity server failed"), + } + } + defer resp.Body.Close() + switch resp.StatusCode { + case 404: + return &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("provided sid or client_secret not found on identity server"), + } + case 400: + return &util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("session has not been validated"), + } + case 200: + return nil + default: + return &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.InternalServerError(), + } + } +} + // UserIDIsWithinApplicationServiceNamespace checks to see if a given userID // 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 @@ -659,6 +733,24 @@ func handleRegistrationFlow( // Add Dummy to the list of completed registration stages AddCompletedSessionStage(sessionID, authtypes.LoginTypeDummy) + case authtypes.LoginTypeEmailIdentity: + if r.Auth.ThreePidCreds == nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("threepidCreds not found in auth field"), + } + } + if err := r.Auth.ThreePidCreds.validate(); err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: err, + } + } + if err := validateEmailIdentity(req.Context(), r.Auth.ThreePidCreds); err != nil { + return *err + } + AddCompletedSessionStage(sessionID, authtypes.LoginTypeApplicationService) + case "": // An empty auth type means that we want to fetch the available // flows. It can also mean that we want to register as an appservice