Add timeout for starting google account pairing

This commit is contained in:
Tulir Asokan 2024-03-13 15:03:48 +02:00
parent 847b9a3a90
commit cf698ed7d6
3 changed files with 45 additions and 10 deletions

View file

@ -159,10 +159,11 @@ func fnLoginGoogle(ce *WrappedCommandEvent) {
} }
const ( const (
pairingErrMsgNoDevices = "No devices found. Make sure you've enabled account pairing in the Google Messages app on your phone." pairingErrMsgNoDevices = "No devices found. Make sure you've enabled account pairing in the Google Messages app on your phone."
pairingErrMsgIncorrectEmoji = "Incorrect emoji chosen on phone, please try again" pairingErrPhoneNotResponding = "Phone not responding. Make sure your phone is connected to the internet and that account pairing is enabled in the Google Messages app."
pairingErrMsgCancelled = "Pairing cancelled on phone" pairingErrMsgIncorrectEmoji = "Incorrect emoji chosen on phone, please try again"
pairingErrMsgTimeout = "Pairing timed out, please try again" pairingErrMsgCancelled = "Pairing cancelled on phone"
pairingErrMsgTimeout = "Pairing timed out, please try again"
) )
func fnLoginGoogleCookies(ce *WrappedCommandEvent) { func fnLoginGoogleCookies(ce *WrappedCommandEvent) {
@ -194,6 +195,8 @@ func fnLoginGoogleCookies(ce *WrappedCommandEvent) {
if err != nil { if err != nil {
if errors.Is(err, libgm.ErrNoDevicesFound) { if errors.Is(err, libgm.ErrNoDevicesFound) {
ce.Reply(pairingErrMsgNoDevices) ce.Reply(pairingErrMsgNoDevices)
} else if errors.Is(err, libgm.ErrPairingInitTimeout) {
ce.Reply(pairingErrPhoneNotResponding)
} else if errors.Is(err, libgm.ErrIncorrectEmoji) { } else if errors.Is(err, libgm.ErrIncorrectEmoji) {
ce.Reply(pairingErrMsgIncorrectEmoji) ce.Reply(pairingErrMsgIncorrectEmoji)
} else if errors.Is(err, libgm.ErrPairingCancelled) { } else if errors.Is(err, libgm.ErrPairingCancelled) {

View file

@ -33,6 +33,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/rs/zerolog"
"go.mau.fi/util/random" "go.mau.fi/util/random"
"golang.org/x/crypto/hkdf" "golang.org/x/crypto/hkdf"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -237,13 +238,16 @@ func (ps *PairingSession) ProcessServerInit(msg *gmproto.GaiaPairingResponseCont
} }
var ( var (
ErrNoCookies = errors.New("gaia pairing requires cookies") ErrNoCookies = errors.New("gaia pairing requires cookies")
ErrNoDevicesFound = errors.New("no devices found for gaia pairing") ErrNoDevicesFound = errors.New("no devices found for gaia pairing")
ErrIncorrectEmoji = errors.New("user chose incorrect emoji on phone") ErrIncorrectEmoji = errors.New("user chose incorrect emoji on phone")
ErrPairingCancelled = errors.New("user cancelled pairing on phone") ErrPairingCancelled = errors.New("user cancelled pairing on phone")
ErrPairingTimeout = errors.New("pairing timed out") ErrPairingTimeout = errors.New("pairing timed out")
ErrPairingInitTimeout = errors.New("client init timed out")
) )
const GaiaInitTimeout = 15 * time.Second
func (c *Client) DoGaiaPairing(ctx context.Context, emojiCallback func(string)) error { func (c *Client) DoGaiaPairing(ctx context.Context, emojiCallback func(string)) error {
if len(c.AuthData.Cookies) == 0 { if len(c.AuthData.Cookies) == 0 {
return ErrNoCookies return ErrNoCookies
@ -277,12 +281,25 @@ func (c *Client) DoGaiaPairing(ctx context.Context, emojiCallback func(string))
if err != nil { if err != nil {
return fmt.Errorf("failed to prepare pairing payloads: %w", err) return fmt.Errorf("failed to prepare pairing payloads: %w", err)
} }
serverInit, err := c.sendGaiaPairingMessage(ctx, ps, gmproto.ActionType_CREATE_GAIA_PAIRING_CLIENT_INIT, clientInit) initCtx, cancel := context.WithTimeout(ctx, GaiaInitTimeout)
serverInit, err := c.sendGaiaPairingMessage(initCtx, ps, gmproto.ActionType_CREATE_GAIA_PAIRING_CLIENT_INIT, clientInit)
cancel()
if err != nil { if err != nil {
cancelErr := c.cancelGaiaPairing(ps)
if cancelErr != nil {
zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to send gaia pairing cancel request after init timeout")
}
if errors.Is(err, context.DeadlineExceeded) {
return ErrPairingInitTimeout
}
return fmt.Errorf("failed to send client init: %w", err) return fmt.Errorf("failed to send client init: %w", err)
} }
pairingEmoji, err := ps.ProcessServerInit(serverInit) pairingEmoji, err := ps.ProcessServerInit(serverInit)
if err != nil { if err != nil {
cancelErr := c.cancelGaiaPairing(ps)
if cancelErr != nil {
zerolog.Ctx(ctx).Warn().Err(err).Msg("Failed to send gaia pairing cancel request after error processing server init")
}
return fmt.Errorf("error processing server init: %w", err) return fmt.Errorf("error processing server init: %w", err)
} }
emojiCallback(pairingEmoji) emojiCallback(pairingEmoji)
@ -317,6 +334,16 @@ func (c *Client) DoGaiaPairing(ctx context.Context, emojiCallback func(string))
return nil return nil
} }
func (c *Client) cancelGaiaPairing(sess PairingSession) error {
return c.sessionHandler.sendMessageNoResponse(SendMessageParams{
Action: gmproto.ActionType_CANCEL_GAIA_PAIRING,
RequestID: sess.UUID.String(),
DontEncrypt: true,
CustomTTL: (300 * time.Second).Microseconds(),
MessageType: gmproto.MessageType_GAIA_2,
})
}
func (c *Client) sendGaiaPairingMessage(ctx context.Context, sess PairingSession, action gmproto.ActionType, msg []byte) (*gmproto.GaiaPairingResponseContainer, error) { func (c *Client) sendGaiaPairingMessage(ctx context.Context, sess PairingSession, action gmproto.ActionType, msg []byte) (*gmproto.GaiaPairingResponseContainer, error) {
respCh, err := c.sessionHandler.sendAsyncMessage(SendMessageParams{ respCh, err := c.sessionHandler.sendAsyncMessage(SendMessageParams{
Action: action, Action: action,

View file

@ -356,6 +356,11 @@ func (prov *ProvisioningAPI) GoogleLoginStart(w http.ResponseWriter, r *http.Req
Error: pairingErrMsgNoDevices, Error: pairingErrMsgNoDevices,
ErrCode: "no-devices-found", ErrCode: "no-devices-found",
}) })
case errors.Is(err, libgm.ErrPairingInitTimeout):
jsonResponse(w, http.StatusBadRequest, Error{
Error: pairingErrPhoneNotResponding,
ErrCode: "timeout",
})
default: default:
jsonResponse(w, http.StatusInternalServerError, Error{ jsonResponse(w, http.StatusInternalServerError, Error{
Error: "Failed to start login", Error: "Failed to start login",