From f6456468c09df5bfc8340c67bc8edbf84b3c50c2 Mon Sep 17 00:00:00 2001 From: Piotr Kozimor Date: Fri, 16 Apr 2021 08:49:12 +0200 Subject: [PATCH] WIP: Handle /register/email/requestToken --- clientapi/routing/register.go | 131 ++++++++++++++++++++++++++++++++-- clientapi/routing/routing.go | 7 ++ sytest-whitelist | 1 + 3 files changed, 133 insertions(+), 6 deletions(-) diff --git a/clientapi/routing/register.go b/clientapi/routing/register.go index 7bbda78f8..7ee308fff 100644 --- a/clientapi/routing/register.go +++ b/clientapi/routing/register.go @@ -16,9 +16,11 @@ package routing import ( + "bytes" "context" "crypto/hmac" "crypto/sha1" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -143,6 +145,15 @@ type registerRequest struct { Type authtypes.LoginType `json:"type"` } +type registerEmailRequestTokenRequest struct { + ClientSecret string `json:"client_secret"` + Email string `json:"email"` + IdAccessToken string `json:"id_access_token,omitempty"` + IdServer string `json:"id_server,omitempty"` + NextLink string `json:"next_link"` + SendAttempt int `json:"send_attempt"` +} + type authDict struct { Type authtypes.LoginType `json:"type"` Session string `json:"session"` @@ -214,6 +225,11 @@ type recaptchaResponse struct { ErrorCodes []int `json:"error-codes"` } +// server response for +type registerEmailRequestTokenResponse struct { + Sid string `json:"sid"` +} + // validateUsername returns an error response if the username is invalid func validateUsername(username string) *util.JSONResponse { // https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161 @@ -340,10 +356,13 @@ func validateEmailIdentity( cred *threepidCreds, // cfg *config.ClientAPI, ) *util.JSONResponse { - url := strings.Join([]string{ - cred.IdServer, - "_matrix/identity/api/v1/3pid/getValidated3pid", - }, "/") + url := fmt.Sprintf( + "https://%s/_matrix/identity/v2/3pid/getValidated3pid", + cred.IdServer) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} req, err := http.NewRequestWithContext(ctx, "POST", url, nil) if err != nil { return &util.JSONResponse{ @@ -355,11 +374,13 @@ func validateEmailIdentity( q.Add("client_secret", cred.ClientSecret) q.Add("sid", cred.Sid) req.URL.RawQuery = q.Encode() - resp, err := http.DefaultClient.Do(req) + req.Header.Add("Authorization", "Bearer swordfish") + resp, err := client.Do(req) if err != nil { + util.GetLogger(ctx).WithError(err).Error("failed conecting to identity server") return &util.JSONResponse{ Code: http.StatusInternalServerError, - JSON: jsonerror.Unknown("validate 3pid on indentity server failed"), + JSON: jsonerror.Unknown("failed conecting to identity server"), } } defer resp.Body.Close() @@ -1084,3 +1105,101 @@ func RegisterAvailable( }, } } + +func RegisterEmailRequestToken( + req *http.Request, + cfg *config.ClientAPI, +) util.JSONResponse { + var r registerEmailRequestTokenRequest + resErr := httputil.UnmarshalJSONRequest(req, &r) + if resErr != nil { + return *resErr + } + idServer := r.IdServer + idAccessToken := r.IdAccessToken + // if !isServerTrusted(idServer, cfg.Matrix.TrustedIDServers) { + // return util.JSONResponse{ + // Code: http.StatusForbidden, + // JSON: jsonerror.NotTrusted(fmt.Sprintf("identity server %s is not trusted.", idServer)), + // } + // } + r.IdServer = "" + r.IdAccessToken = "" + return requestEmailTokenFromIdServer(req.Context(), idServer, idAccessToken, &r) +} + +func isServerTrusted(s string, trusted []string) bool { + for _, t := range trusted { + if s == t { + return true + } + } + return false +} + +func requestEmailTokenFromIdServer( + ctx context.Context, + idServer, idAccessToken string, + r *registerEmailRequestTokenRequest, +) util.JSONResponse { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + b := bytes.Buffer{} + enc := json.NewEncoder(&b) + err := enc.Encode(r) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("enc.Encode failed") + return jsonerror.InternalServerError() + } + idReq, err := http.NewRequest( + // ctx, + "POST", + fmt.Sprintf( + "https://%s/_matrix/identity/v2/validate/email/requestToken", + idServer), + &b) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("http.NewRequestWithContext failed") + return jsonerror.InternalServerError() + } + idReq.Header.Add("Authentication", "Bearer "+idAccessToken) + idReq.Header.Add("Content-Type", "application/json") + // util.GetLogger(ctx).WithError(err).Warnf("buffer %s") + // io.Copy(os.Stdout, &b) + idResp, err := client.Do(idReq) + if err != nil { + util.GetLogger(ctx).WithError(err).Errorf("failed to connect to identity server %s", idServer) + return jsonerror.InternalServerError() + } + defer idResp.Body.Close() + decoder := json.NewDecoder(idResp.Body) + if idResp.StatusCode < 300 { + var resp registerEmailRequestTokenResponse + decoder.Decode(&resp) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("failed to decode identity server response body") + return jsonerror.InternalServerError() + } + return util.JSONResponse{ + Code: idResp.StatusCode, + JSON: resp, + } + } + if idResp.StatusCode < 500 { + var resp jsonerror.MatrixError + decoder.Decode(&resp) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("failed to decode identity server response body") + return jsonerror.InternalServerError() + } + return util.JSONResponse{ + Code: idResp.StatusCode, + JSON: resp, + } + } + // We got 500 status code from indentity server + util.GetLogger(ctx).WithError(err).Error("identity server internal server error") + return jsonerror.InternalServerError() +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 9f980e0a9..0022f5161 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -312,6 +312,13 @@ func Setup( return RegisterAvailable(req, cfg, accountDB) })).Methods(http.MethodGet, http.MethodOptions) + r0mux.Handle("/register/email/requestToken", httputil.MakeExternalAPI("registerEmailRequestToken", func(req *http.Request) util.JSONResponse { + if r := rateLimits.rateLimit(req); r != nil { + return *r + } + return RegisterEmailRequestToken(req, cfg) + })).Methods(http.MethodPost, http.MethodOptions) + r0mux.Handle("/directory/room/{roomAlias}", httputil.MakeExternalAPI("directory_room", func(req *http.Request) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) diff --git a/sytest-whitelist b/sytest-whitelist index 8c4585716..35e4b3a25 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -71,6 +71,7 @@ GET /rooms/:room_id/joined_members fetches my membership Both GET and PUT work POST /rooms/:room_id/read_markers can create read marker User signups are forbidden from starting with '_' +Can register using an email address via identity server Request to logout with invalid an access token is rejected Request to logout without an access token is rejected Room creation reports m.room.create to myself