From f37169d4d57aa64e47afdfbf2f44a09e1337d559 Mon Sep 17 00:00:00 2001 From: ayn2op Date: Wed, 4 Mar 2026 12:14:46 -0500 Subject: [PATCH] refactor(login): clean up qr flow and related state transitions --- internal/ui/login/qr.go | 63 +++++++++++------------------------------ 1 file changed, 17 insertions(+), 46 deletions(-) diff --git a/internal/ui/login/qr.go b/internal/ui/login/qr.go index 151b156..9ef525c 100644 --- a/internal/ui/login/qr.go +++ b/internal/ui/login/qr.go @@ -1,7 +1,6 @@ package login import ( - "bytes" "context" "crypto/rand" "crypto/rsa" @@ -21,7 +20,7 @@ import ( apphttp "github.com/ayn2op/discordo/internal/http" "github.com/ayn2op/discordo/internal/ui" "github.com/ayn2op/tview" - "github.com/diamondburned/arikawa/v3/api" + "github.com/diamondburned/arikawa/v3/utils/httputil" "github.com/gdamore/tcell/v3" "github.com/gorilla/websocket" "github.com/skip2/go-qrcode" @@ -29,8 +28,6 @@ import ( const gatewayURL = "wss://remote-auth-gateway.discord.gg/?v=2" -var endpointRemoteAuthLogin = api.EndpointMe + "/remote-auth/login" - type qrLogin struct { *tview.TextView app *tview.Application @@ -169,12 +166,11 @@ func (q *qrLogin) run(ctx context.Context) { } encodedPublicKey := base64.StdEncoding.EncodeToString(pubDER) - headers := stdhttp.Header{} - headers.Set("User-Agent", apphttp.BrowserUserAgent) - headers.Set("Origin", "https://discord.com") - q.setText("Connecting to Remote Auth Gateway...\n\nPress Esc to cancel") + headers := apphttp.Headers() + headers.Set("User-Agent", apphttp.BrowserUserAgent) + dialer := websocket.Dialer{ Proxy: stdhttp.ProxyFromEnvironment, HandshakeTimeout: 15 * time.Second, @@ -398,55 +394,31 @@ func exchangeTicket(ctx context.Context, ticket string, fingerprint string, priv if ticket == "" { return "", errors.New("empty ticket") } - body := map[string]string{"ticket": ticket} - raw, err := json.Marshal(body) - if err != nil { - return "", err - } - req, err := stdhttp.NewRequestWithContext(ctx, stdhttp.MethodPost, endpointRemoteAuthLogin, bytes.NewReader(raw)) - if err != nil { - return "", err - } - req.Header = apphttp.Headers() - req.Header.Set("Content-Type", "application/json") - req.Header.Set("User-Agent", apphttp.BrowserUserAgent) + headers := apphttp.Headers() if fingerprint != "" { - req.Header.Set("X-Fingerprint", fingerprint) - req.Header.Set("Referer", "https://discord.com/ra/"+fingerprint) + headers.Set("X-Fingerprint", fingerprint) + headers.Set("Referer", "https://discord.com/ra/"+fingerprint) } - client := &stdhttp.Client{Transport: apphttp.NewTransport(), Timeout: 20 * time.Second} - resp, err := client.Do(req) + // Create an API client without a token. + client := apphttp.NewClient("") + client.OnRequest = append(client.OnRequest, httputil.WithHeaders(headers)) + + token, err := client.ExchangeRemoteAuthTicket(ticket) if err != nil { return "", err } - defer resp.Body.Close() - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - b, _ := io.ReadAll(resp.Body) - return "", fmt.Errorf("remote-auth login failed: %s: %s", resp.Status, string(b)) - } - decoder := json.NewDecoder(resp.Body) - - var out struct { - EncryptedToken string `json:"encrypted_token"` - } - if err := decoder.Decode(&out); err != nil { - return "", err - } - if out.EncryptedToken == "" { - return "", fmt.Errorf("no encrypted_token in response") - } - enc, err := base64.StdEncoding.DecodeString(out.EncryptedToken) + decodedToken, err := base64.StdEncoding.DecodeString(token) if err != nil { return "", err } - pt, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, enc, nil) + decryptedToken, err := rsa.DecryptOAEP(sha256.New(), nil, priv, decodedToken, nil) if err != nil { return "", err } - return string(pt), nil + return string(decryptedToken), nil } func (q *qrLogin) success(token string) { @@ -463,9 +435,8 @@ func (q *qrLogin) fail(err error) { } func mapWSCloseError(err error) error { - var cerr *websocket.CloseError - if errors.As(err, &cerr) { - switch cerr.Code { + if err, ok := errors.AsType[*websocket.CloseError](err); ok { + switch err.Code { case 1000: return errors.New("session closed") case 4000: