From c2d94947ee01448f078fd62b106bab82041254ca Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Tue, 27 Aug 2024 15:45:25 -0600 Subject: [PATCH] provisioning: implement legacy QR endpoint Signed-off-by: Sumner Evans --- cmd/mautrix-telegram/legacyprovisioning.go | 88 ++++++++++++++++++++++ cmd/mautrix-telegram/main.go | 17 ++++- go.mod | 4 +- go.sum | 4 +- 4 files changed, 108 insertions(+), 5 deletions(-) diff --git a/cmd/mautrix-telegram/legacyprovisioning.go b/cmd/mautrix-telegram/legacyprovisioning.go index 9ae1e77c..075c586d 100644 --- a/cmd/mautrix-telegram/legacyprovisioning.go +++ b/cmd/mautrix-telegram/legacyprovisioning.go @@ -17,11 +17,13 @@ package main import ( + "context" "encoding/json" "fmt" "net/http" "sync" + "github.com/gorilla/websocket" "github.com/rs/zerolog" "go.mau.fi/util/exhttp" "maunium.net/go/mautrix/bridgev2" @@ -62,6 +64,92 @@ type legacyLogin struct { var inflightLegacyLoginsLock sync.RWMutex var inflightLegacyLogins = map[id.UserID]*legacyLogin{} +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + Subprotocols: []string{"net.maunium.telegram.login"}, +} + +func legacyProvLoginQR(w http.ResponseWriter, r *http.Request) { + log := zerolog.Ctx(r.Context()).With().Str("prov_method", "qr_login").Logger() + ctx := log.WithContext(r.Context()) + + user := m.Matrix.Provisioning.GetUser(r) + resp := response{Username: user.MXID} + + var err error + var loginProcess bridgev2.LoginProcess + var nextStep *bridgev2.LoginStep + if loginProcess, err = c.CreateLogin(ctx, user, connector.LoginFlowIDQR); err != nil { + exhttp.WriteJSONResponse(w, http.StatusInternalServerError, resp.WithError("create_login_failed", fmt.Sprintf("Failed to create a QR login process: %s", err.Error()))) + } else if nextStep, err = loginProcess.Start(ctx); err != nil { + exhttp.WriteJSONResponse(w, http.StatusInternalServerError, resp.WithError("start_login_failed", fmt.Sprintf("Failed to start login process: %s", err.Error()))) + } else if nextStep.StepID != connector.LoginStepIDShowQR { + exhttp.WriteJSONResponse(w, http.StatusInternalServerError, resp.WithError("unexpected_step", fmt.Sprintf("Unexpected first step %s", nextStep.StepID))) + } + + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Err(err).Msg("Failed to upgrade connection to websocket") + return + } + defer func() { + err := ws.Close() + if err != nil { + log.Debug().Err(err).Msg("Error closing websocket") + } + }() + + go func() { + // Read everything so SetCloseHandler() works + for { + _, _, err = ws.ReadMessage() + if err != nil { + break + } + } + }() + ctx, cancel := context.WithCancel(context.Background()) + ws.SetCloseHandler(func(code int, text string) error { + log.Debug().Int("close_code", code).Msg("Login websocket closed, cancelling login") + cancel() + return nil + }) + + for { + switch nextStep.StepID { + case connector.LoginStepIDShowQR: + nextStep, err = loginProcess.(bridgev2.LoginProcessDisplayAndWait).Wait(ctx) + if err != nil { + ws.WriteJSON(map[string]any{ + "success": false, + "error": "qr_login_failed", + "message": fmt.Sprintf("Failed to login using QR code: %s", err), + }) + return + } + ws.WriteJSON(map[string]any{"code": nextStep.DisplayAndWaitParams.Data}) + case connector.LoginStepIDComplete: + ws.WriteJSON(map[string]any{"success": true}) + return + case connector.LoginStepIDPassword: + inflightLegacyLoginsLock.Lock() + inflightLegacyLogins[user.MXID] = &legacyLogin{Process: loginProcess, NextStep: nextStep} + inflightLegacyLoginsLock.Unlock() + ws.WriteJSON(map[string]any{"success": false, "error": "password-needed"}) + return + default: + ws.WriteJSON(map[string]any{ + "success": false, + "error": "unexpected_step", + "message": fmt.Sprintf("Unexpected step in QR code login process %s", nextStep.StepID), + }) + return + } + } +} + func legacyProvLoginRequestCode(w http.ResponseWriter, r *http.Request) { log := zerolog.Ctx(r.Context()).With().Str("prov_step", "request_code").Logger() ctx := log.WithContext(r.Context()) diff --git a/cmd/mautrix-telegram/main.go b/cmd/mautrix-telegram/main.go index 1e122a86..915ac226 100644 --- a/cmd/mautrix-telegram/main.go +++ b/cmd/mautrix-telegram/main.go @@ -19,6 +19,8 @@ package main import ( "encoding/base64" "fmt" + "net/http" + "strings" "go.mau.fi/util/dbutil/litestream" "maunium.net/go/mautrix/bridgev2/bridgeconfig" @@ -60,7 +62,20 @@ func main() { versionWithoutCommit := m.Version m.PostStart = func() { if m.Matrix.Provisioning != nil { - // m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/qr", legacyProvLoginQR) + m.Matrix.Provisioning.GetAuthFromRequest = func(r *http.Request) string { + if !strings.HasSuffix(r.URL.Path, "/login/qr") { + return "" + } + authParts := strings.Split(r.Header.Get("Sec-WebSocket-Protocol"), ",") + for _, part := range authParts { + part = strings.TrimSpace(part) + if strings.HasPrefix(part, "net.maunium.telegram.auth-") { + return strings.TrimPrefix(part, "net.maunium.telegram.auth-") + } + } + return "" + } + m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/qr", legacyProvLoginQR) m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/request_code", legacyProvLoginRequestCode) m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/send_code", legacyProvLoginSendCode) m.Matrix.Provisioning.Router.HandleFunc("/v1/user/{userID}/login/send_password", legacyProvLoginSendPassword) diff --git a/go.mod b/go.mod index 01951101..0a775f05 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module go.mau.fi/mautrix-telegram go 1.22 require ( + github.com/gorilla/websocket v1.5.3 github.com/gotd/td v0.105.0 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 @@ -11,7 +12,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/net v0.28.0 - maunium.net/go/mautrix v0.20.1-0.20240827134950-e3eb2953ddda + maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8 ) require ( @@ -23,7 +24,6 @@ require ( github.com/go-faster/jx v1.1.0 // indirect github.com/go-faster/xor v1.0.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/gotd/ige v0.2.2 // indirect github.com/gotd/neo v0.1.5 // indirect github.com/klauspost/compress v1.17.9 // indirect diff --git a/go.sum b/go.sum index f3e84e16..5e597ea0 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= -maunium.net/go/mautrix v0.20.1-0.20240827134950-e3eb2953ddda h1:3n7LQS2LQFW4uNUeECEUNWCCCaNTygk0CGSE+CZtEqo= -maunium.net/go/mautrix v0.20.1-0.20240827134950-e3eb2953ddda/go.mod h1:7hh/Hx5W9lUcqL0hkSw52kMyY+6nMUPTtdDN0qVEXwI= +maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8 h1:k+HkbFrExb508ofb+Snz+yGa1/GQ6WKqyGzGYXSzT3A= +maunium.net/go/mautrix v0.20.1-0.20240827221252-892e5cf01fc8/go.mod h1:7hh/Hx5W9lUcqL0hkSw52kMyY+6nMUPTtdDN0qVEXwI= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=