mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
OKX: Fix authenticated websocket login (#2051)
* Okx:remove * Okx:replace * Okx:ping gws.PingMessage * Okx:ping gws.PingMessage * Okx: add authenticateConnection * Okx: fix pingHandler * Update exchanges/okx/okx_business_websocket.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/okx/okx_business_websocket.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/okx/okx_websocket.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/okx/okx_business_websocket.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Okx:UseMultiConnectionManagement * Okx:rm UseMultiConnectionManagement * Okx:roll back * Okx:apply diff * Okx:make lint fix * Okx:make lint fix * Okx:make lint fix * Okx:fix * Okx:fix name * Okx:fix NilGuard depends on #2076 * Okx:remove comment --------- Co-authored-by: Ryan O'Hara-Reid <oharareid.ryan@gmail.com> Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>
This commit is contained in:
@@ -2,16 +2,12 @@ package okx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gws "github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/websocket"
|
||||
@@ -55,15 +51,13 @@ func (e *Exchange) WsConnectBusiness(ctx context.Context) error {
|
||||
dialer.WriteBufferSize = 8192
|
||||
|
||||
e.Websocket.Conn.SetURL(okxBusinessWebsocketURL)
|
||||
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
|
||||
if err != nil {
|
||||
if err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{}); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Websocket.Wg.Add(1)
|
||||
go e.wsReadData(ctx, e.Websocket.Conn)
|
||||
e.Websocket.Wg.Go(func() { e.wsReadData(ctx, e.Websocket.Conn) })
|
||||
|
||||
if e.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Successful connection to %v\n",
|
||||
e.Websocket.GetWebsocketURL())
|
||||
log.Debugf(log.ExchangeSys, "Successful connection to %v", e.Websocket.GetWebsocketURL())
|
||||
}
|
||||
e.Websocket.Conn.SetupPingHandler(request.UnAuth, websocket.PingHandler{
|
||||
MessageType: gws.TextMessage,
|
||||
@@ -71,45 +65,14 @@ func (e *Exchange) WsConnectBusiness(ctx context.Context) error {
|
||||
Delay: time.Second * 20,
|
||||
})
|
||||
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
err = e.WsSpreadAuth(ctx)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys, "Error connecting auth socket: %s\n", err.Error())
|
||||
if err := e.authenticateConnection(ctx, e.Websocket.Conn); err != nil { // business WS uses same conn for public and private
|
||||
log.Errorf(log.ExchangeSys, "Error authenticating business websocket: %s", err)
|
||||
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WsSpreadAuth will connect to Okx's Private websocket connection and Authenticate with a login payload.
|
||||
func (e *Exchange) WsSpreadAuth(ctx context.Context) error {
|
||||
if !e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", e.Name)
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
ts := time.Now().Unix()
|
||||
signPath := "/users/self/verify"
|
||||
hmac, err := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(strconv.FormatInt(ts, 10)+http.MethodGet+signPath),
|
||||
[]byte(creds.Secret),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []WebsocketLoginData{
|
||||
{
|
||||
APIKey: creds.Key,
|
||||
Passphrase: creds.ClientID,
|
||||
Timestamp: ts,
|
||||
Sign: base64.StdEncoding.EncodeToString(hmac),
|
||||
},
|
||||
}
|
||||
return e.SendAuthenticatedWebsocketRequest(ctx, request.Unset, "login-response", operationLogin, args, nil)
|
||||
}
|
||||
|
||||
// GenerateDefaultBusinessSubscriptions returns a list of default subscriptions to business websocket.
|
||||
func (e *Exchange) GenerateDefaultBusinessSubscriptions() ([]subscription.Subscription, error) {
|
||||
var subs []string
|
||||
|
||||
@@ -3171,6 +3171,12 @@ type WebsocketLoginData struct {
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
// WebsocketAuthLogin holds the operation and arguments
|
||||
type WebsocketAuthLogin struct {
|
||||
Operation string `json:"op"`
|
||||
Arguments []WebsocketLoginData `json:"args"`
|
||||
}
|
||||
|
||||
// SubscriptionInfo holds the channel and instrument IDs
|
||||
type SubscriptionInfo struct {
|
||||
Channel string `json:"channel,omitempty"`
|
||||
|
||||
@@ -41,7 +41,7 @@ var (
|
||||
// See: https://www.okx.com/docs-v5/en/#error-code-websocket-public
|
||||
authConnErrorCodes = []string{
|
||||
"60007", "60022", "60023", "60024", "60026", "63999", "60032", "60011", "60009",
|
||||
"60005", "60021", "60031", "50110",
|
||||
"60005", "60021", "60031", "50110", "60033",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -245,15 +245,12 @@ func (e *Exchange) WsConnect() error {
|
||||
dialer.ReadBufferSize = 8192
|
||||
dialer.WriteBufferSize = 8192
|
||||
|
||||
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
|
||||
if err != nil {
|
||||
if err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{}); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Websocket.Wg.Add(1)
|
||||
go e.wsReadData(ctx, e.Websocket.Conn)
|
||||
e.Websocket.Wg.Go(func() { e.wsReadData(ctx, e.Websocket.Conn) })
|
||||
if e.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Successful connection to %v\n",
|
||||
e.Websocket.GetWebsocketURL())
|
||||
log.Debugf(log.ExchangeSys, "Successful connection to %v", e.Websocket.GetWebsocketURL())
|
||||
}
|
||||
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
|
||||
MessageType: gws.TextMessage,
|
||||
@@ -261,8 +258,7 @@ func (e *Exchange) WsConnect() error {
|
||||
Delay: time.Second * 20,
|
||||
})
|
||||
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
err = e.WsAuth(ctx)
|
||||
if err != nil {
|
||||
if err := e.WsAuth(ctx); err != nil {
|
||||
log.Errorf(log.ExchangeSys, "Error connecting auth socket: %s\n", err.Error())
|
||||
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
@@ -275,24 +271,24 @@ func (e *Exchange) WsAuth(ctx context.Context) error {
|
||||
if !e.AreCredentialsValid(ctx) || !e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", e.Name)
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dialer gws.Dialer
|
||||
err = e.Websocket.AuthConn.Dial(ctx, &dialer, http.Header{})
|
||||
if err != nil {
|
||||
if err := e.Websocket.AuthConn.Dial(ctx, &dialer, http.Header{}); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Websocket.Wg.Add(1)
|
||||
go e.wsReadData(ctx, e.Websocket.AuthConn)
|
||||
e.Websocket.Wg.Go(func() { e.wsReadData(ctx, e.Websocket.AuthConn) })
|
||||
e.Websocket.AuthConn.SetupPingHandler(request.Unset, websocket.PingHandler{
|
||||
MessageType: gws.TextMessage,
|
||||
Message: pingMsg,
|
||||
Delay: time.Second * 20,
|
||||
})
|
||||
return e.authenticateConnection(ctx, e.Websocket.AuthConn)
|
||||
}
|
||||
|
||||
e.Websocket.SetCanUseAuthenticatedEndpoints(true)
|
||||
func (e *Exchange) authenticateConnection(ctx context.Context, conn websocket.Connection) error {
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts := time.Now().Unix()
|
||||
signPath := "/users/self/verify"
|
||||
hmac, err := crypto.GetHMAC(crypto.HashSHA256,
|
||||
@@ -303,21 +299,37 @@ func (e *Exchange) WsAuth(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []WebsocketLoginData{
|
||||
{
|
||||
APIKey: creds.Key,
|
||||
Passphrase: creds.ClientID,
|
||||
Timestamp: ts,
|
||||
Sign: base64.StdEncoding.EncodeToString(hmac),
|
||||
op := WebsocketAuthLogin{
|
||||
Operation: operationLogin,
|
||||
Arguments: []WebsocketLoginData{
|
||||
{
|
||||
APIKey: creds.Key,
|
||||
Passphrase: creds.ClientID,
|
||||
Timestamp: ts,
|
||||
Sign: base64.StdEncoding.EncodeToString(hmac),
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := conn.SendMessageReturnResponse(ctx, request.Unset, "login-response", op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var intermediary struct {
|
||||
Code int64 `json:"code,string"`
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
if err := json.Unmarshal(resp, &intermediary); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.SendAuthenticatedWebsocketRequest(ctx, request.Unset, "login-response", operationLogin, args, nil)
|
||||
if intermediary.Code != 0 {
|
||||
return getStatusError(intermediary.Code, intermediary.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsReadData sends msgs from public and auth websockets to data handler
|
||||
func (e *Exchange) wsReadData(ctx context.Context, ws websocket.Connection) {
|
||||
defer e.Websocket.Wg.Done()
|
||||
for {
|
||||
resp := ws.ReadMessage()
|
||||
if resp.Raw == nil {
|
||||
|
||||
Reference in New Issue
Block a user