sorare / api

Sorare API documentation
156 stars 49 forks source link

Websocket subscription not recognizing APIKey, cannot run query with complexity > 5 #309

Closed juliusdanek closed 1 year ago

juliusdanek commented 2 years ago

The following query will not fire events if I add the deal object. We are trying to extract some information from it (such as the id). Would be great if we could get this to work.

Thanks!

  tokenOfferWasUpdated(sports: [BASEBALL]) {
    acceptedAt
    senderSide {
      nfts {
        slug
        id
        assetId
        collectionName
        pictureUrl
        name
        contractAddress
        ethereumId
        owner {
          address
          blockchain
        }
        ownershipHistory {
          address
          blockchain
          from
          id
          deal {
            ... on TokenOffer {
              id
            }
          }          
          priceWei
          transferType
        }
        metadata {
          ... on TokenCardMetadataInterface {
            teamSlug
            rarity
            serialNumber
            playerSlug
            playerDisplayName
            seasonStartYear
            ... on TokenBaseballMetadata {
              playerPositions
            }
          }
        }
      }
    }
  }
}
redox commented 2 years ago

Hey @juliusdanek ,

I finally had time to dig your problem a bit deeper and I'm not sure I get the problem. On my end, using your query and an authenticated WebSocket session I can see the deal object being retrieved:

image

Do you mind sharing more details about the problem?

juliusdanek commented 2 years ago

Hey @redox you are right, the issue is on my end. We never properly authenticated with the subscriptions. I have tried a couple of options with 3 different Python packages but I am unable to add the authentication. I attach a header to the request with my API key in the form of {"APIKEY": "******"}. Also raised this issue with @leszekhanusz: https://github.com/leszekhanusz/gql-actioncable/issues/2.

Are special API keys required for subscriptions or do the regular ones work as well?

juliusdanek commented 2 years ago

@redox update here:

At least with Python, it doesn't work to only authenticate yourself via the APIKEY header for the subscription. Additionally, you need to actually sign in with your Sorare account and generate the necessary cookies and the CSRF token and attach those as headers as well.

In my mind this is unexpected behavior. Like all other endpoints, it should be possible to authenticate with APIKEY only if you are looking to access publicly available data. Let me know what you think.

I also tried out attaching the headers below which also don't work.

{"APIKEY": "****",
"Authorization": "Bearer *****"}
YoanGab commented 2 years ago

I have exactly same behavior as @juliusdanek. I have tried several authentication methods with the subscription in Python and it doesn't seem to work as it is documented.

The only way I found to make it work is as he said to connect to a Sorare account and retrieve informations from that request and pass them into headers.

djizus commented 2 years ago

Hello, to authenticate properly you have to put the AUD in the headers as well. This works :

#gql setup
headers = CaseInsensitiveDict()
headers["content-type"] = "application/json"
headers["APIKEY"] = apiKey
headers["Authorization"] = 'Bearer ' + sorareToken
headers["JWT-AUD"] = audName

#webservice setup
w_socket = 'wss://ws.sorare.com/cable'
identifier = json.dumps({"channel": "GraphqlChannel"})

subscription_query = gql(
    """
    xxxxx
    """
)
juliusdanek commented 2 years ago

Hey @djizus.

  1. What is the AUD?
  2. This is not expected behavior. I shouldn't have to log-in in order to authenticate with the subscription API. API Key should be enough.
djizus commented 2 years ago

You submit one when you follow the documentation.

Using this request allows you to generate the aud string (which you should set up to be your application name) : mutation SignInMutation($input: signInInput!) { signIn(input: $input) { currentUser { slug jwtToken(aud: "<YourAud>") { token expiredAt } } errors { message } } }

Then you get the token which is linked to the aud (application name). In order to use it properly, you need both token+AUD in addition to the the API_KEY to form your request headers.

You can generate a new AUD if you forgot the one you registered by using the sign-in mutation again.

And you never have to log-in again, my subscriptions have been running just fine for 6 months.

juliusdanek commented 2 years ago

Hey @djizus just tried, still the same issue. Able to generate the access token correctly with an AUD and now we are passing the following headers:

{"APIKEY": $SORARE_API_KEY,
 "JWT-AUD": $AUD,
 "Authorization": "Bearer $ACCESS_TOKEN",
 "content-type": "application/json"}

Any idea why this is not working?

djizus commented 2 years ago

Hi @juliusdanek , looks good to me ! What kind of error do you get if you try a simple request like the following one ?

query CurentUserQuery {
  currentUser {
    starkKey
  }
}

For reference, here are my headers (in python) and they look exactly the same :

headers = CaseInsensitiveDict()
headers["content-type"] = "application/json"
headers["APIKEY"] = apiKey
headers["Authorization"] = 'Bearer ' + sorareToken
headers["JWT-AUD"] = audName
juliusdanek commented 2 years ago

It simply throws an error telling me that I am not authorized to do queries with a complexity over 5. Which is weird, because the API key works for everything else.

I get a correct response when I run the above. Log-in works with no problem, I'm also able to run queries with a complexity above 5 using the normal endpoints. However, when I try the websockets / subscriptions, the complexity error immediately pops-up.

djizus commented 2 years ago

Ok, weird ! It may come from your gql connection. I can provide you my python code that works using gqlactioncable, otherwise I'm out of ideas :

@backoff.on_exception(backoff.expo, Exception, giveup=lambda e: isinstance(e, TransportQueryError))
async def execute_subscription(session):
    logger.warning("#####Starting new subscription#####")
    async for message in session.subscribe(subscription_query):
        ##do something

async def main():
    wsClient = Client(
        transport=ActionCableWebsocketsTransport(
            url=w_socket,
            keep_alive_timeout=300,
            headers=headers,
        )
    )
    session = await wsClient.connect_async(reconnecting=True)
    task = asyncio.create_task(execute_subscription(session))
    await task

asyncio.run(main())
jeremyallencpa commented 1 year ago

Hey @juliusdanek ,

I finally had time to dig your problem a bit deeper and I'm not sure I get the problem. On my end, using your query and an authenticated WebSocket session I can see the deal object being retrieved:

image

Do you mind sharing more details about the problem?

Hi @redox I am having this exact same issue when I try to run this query.

I am following the documentation and examples in the examples folder but the query breaks when deal is added.

It looks like you were able to get it working in this screenshot.

Can you send your code you're using and/or add it to the examples folder in the repository?

This is my code for implementing the ActionCable (matches the documentation exactly):

const cable = new ActionCable({
    url: "wss://ws.sorare.com/cable",
    headers: {
      Authorization: `Bearer ***`,
      APIKEY:
        "***",
    },
  });

It would be helpful to see what I'm doing wrong by seeing your implementation that you have working in this screenshot.

Do I perhaps need a special API key specifically for the Websocket to get depth > 5?

Scarl3tBeg0nias commented 1 year ago

subscription_query

I am also having this issue.

My API key works for fine everything, including complex queries which are greater than 5 in complexity.

The only instance where it does not work is for subscriptions, and I am following the examples exactly as shown in the repository.

Is anyone able to run a query with complexity greater than 5 specifically for subscriptions and if so can you share your implementation (with sensitive information redacted such as API key)?

ronando82 commented 1 year ago

same issue than describe @Scarl3tBeg0nias

it looks like the headers are ignored..

juliusdanek commented 1 year ago

@eulbat @redox can you guys help out here? This affects quite a few people and the workarounds don't always work.

redox commented 1 year ago

Hey @juliusdanek @ronando82 @Scarl3tBeg0nias @jeremyallencpa good news \o/ We fixed the problem with our WebSocket servers and it's now acomodating with the APIKEY header. So if you pass the APIKEY header to your websocket subscription you'll get extra GQL complexity/depth limits.

Thank you for your patience!

ronando82 commented 1 year ago

thanks @redox. Works great !

rbdm-qnt commented 1 year ago

thanks @redox. Works great !

Is that script still working for you? Me and many other users were not able to use authenticated WSS connections in the last several months. Could you share your subscription script?