CORS issue with client side Realm GraphQL Endpoint

Hi everyone,

First off - Kudos to the Realm team for building a great product. I am having a CORS issue when working with the GraphQL endpoint from the browser and hoping someone can lead me in the right direction. My issue is this: (apologies for the somewhat long explanation but I wanted to be as detailed as I could)

I am using a custom JWT Authentication solution, so I currently maintain all tokens for identity etc already in my app. I am able to setup a Custom JWT Authentication Authentication provider and input the corresponding JWK URI successfully in the Realm dashboard. It works fine after testing.

The problem I have is then trying to pass in the JWT in the header of the post request to Realm GraphQL endpoint https://realm.mongodb.com/api/client/v2.0/app//graphql from the browser using fetch. When I do so I get the below CORS error:

Access to fetch at ‘https://realm.mongodb.com/api/client/v2.0/app//graphql’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

Please note that I am not currently using the realm-web package as I do not want to add an additional JWT token layer (realm-web maintains their own JWT token system in local storage) to my app that already has its own. I simply want to include the JWT token in the header of a fetch request to then have Realm verify against the JWK URI setup in the Realm dashboard. The docs here Authenticate GraphQL Requests make it seem like this is entirely possible as it notes that you can pass the custom JWT trough as a jwtTokenString header using a normal http request. However, I still get the CORS error when attempting this request.

I have also tried whitelisting my http://localhost:3000 in the Realm dashboard under Manage > Settings > Allowed Request Origins. Still not able to get it to work.

I was able to get several less than ideal workarounds working:

  1. Using another server as a proxy - since a node.js environment doesn’t have these CORS restrictions I was able to pass the token through to another endpoint which fetched the Realm GraphQL endpoint from a server environment successfully. (Not great as that essentially creates 2 servers)

  2. I was able to use both the realm-web package and my own system to make a more complicated auth system in which the realm-web package gave me a Bearer token to use without any CORS issues. (Not ideal as that requires 2 token systems)

Potential Solutions:
I’m not positive on why the error is happening but I believe it may have something to do with the following that someone on the Realm team would have more context on:

Are the URLS whitelisted in Allowed Request Origins mapped to the approved URLS for CORS requests?
Is the Access-Control-Allow-Headers option on the wherever the Realm server is hosted allowing a header of jwtTokenString ?

Really appreciate anyone who can help lead me in the right direction on this.

Thank you!

2 Likes

Hi @Matt_Cunningham,

Are you certain you use a PSOT method to run a qraphql query?

Using an HTTPs API to run the query should be done via a POST method as defined here:

Also you can use the Bearer method with an Access token you get from your JWT provider.

I have noticed those CORS issues when trying to use other HTTP methods like “GET”.

Best regards,
Pavel

Hi @Pavel_Duchovny thanks for the reply, yes I am using a POST request and I am confident I am running the right query because it works in a server environment. When I switch to the browser with the same query it gives CORS issues. I am using something like this, passing in the JWT as jwtTokenString shown here Custom JWT

    const result = await fetch(
      `https://myrealmgraphqlendpoint`,
      {
        method: "POST",
        headers: {
          jwtTokenString: user.token,
        },
        body: JSON.stringify({ query: FIND_MOVIES }),
      }
    );
    const json = await result.json();
    console.log(json);

Another interesting note, I found these docs Authenticate HTTP Client Requests which states

MongoDB Realm enforces non-sync rules and sync rules for all client operations. This means that requests must be made by a logged in user of your Realm app.

And those docs also offer the ability to get a Client API Access Token through the login endpoint.

But I am curious what that means - does that mean I must use the realm-web package or ping the Realm login endpoint to receive a Realm generated Bearer token?

In my case I am still wondering if I am able to just provide my custom JWT along with all of my requests to the endpoint and not have to generate another Bearer token through the login endpoint or realm-web package. Does that make sense?

1 Like

Hi @Matt_Cunningham,

I think it should work both ways otherwise it might be a bug.

https://docs.mongodb.com/realm/graphql/authenticate/#custom-jwt

You should be able to provide credentials to do both auth + query. It can be email/password but also jwt token.
https://docs.mongodb.com/realm/graphql/authenticate/#custom-jwt

Out of curiosity does a bearer token with access token doesn’t yield cors errors?

Best
Pavel

@Pavel_Duchovny Yeah I think it may be a bug. So I tried using Bearer with my JWT from my provider (not one assigned from Realm) like so:

  const result = await fetch(
      `https://myrealmgraphqlendpoint`,
      {
        method: "POST",
        headers: {
          Authorization: "Bearer " + user.token, 
        },
        body: JSON.stringify({ query: FIND_MOVIES }),
      }
    );

But now I get the below error in the console, which is probably because the Bearer strategy on the Realm endpoint is expecting a Realm assigned token format, not custom JWT format which maybe is why the jwtTokenString header exists in the first place.

{
  "error": "value of 'kid' has invalid format",
  "link": "https://realm.mongodb.com/groups/5f3ab9628951c83aa903a0b0/apps/5f5d28bcdda1ce73d48eaa42/logs?co_id=5f5f9cb6a93317dab797e984"
}

If it is a bug, I am happy to write out any further steps to reproduce. Let me know!

@Matt_Cunningham,

The bearer token can’t be your custom one but only realms.

I asked you to test it in 2 steps get a realm access token from custom-jwt/login endpoint and use it in the bearer.

Pavel

Hi @Pavel_Duchovny,

The 2 steps process works when I use the login endpoint - the login endpoint responds with an access_token and refresh_token etc. Using the provided access_token I am able to to query using the Bearer method successfully. While that process works and gives no CORS errors, it would mean I have to deal with handling refreshes of my own custom JWTs as well as the access_token that is provided back in the payload from the Realm login endpoint. Sorry if I am being repetitive here, but as you mentioned in your earlier reply

You should be able to provide credentials to do both auth + query. It can be email/password but also jwt token.

Which means there should be a scenario where I wouldn’t have to use the login endpoint at all correct?

Hi @Pavel_Duchovny,

I’m facing the same problem. I’m using an apiKey header and I’m getting the same error @Matt_Cunningham is getting.

The thing is when I try the endpoint using Postman it works but when I use Apollo client with Angular it does not!!

I hope that you have any idea about whats happening. :frowning:

Hey Hadi -

I’m assuming you’re running into this error because you’re calling it from the browser. Please consider using authorization headers https://docs.mongodb.com/realm/graphql/authenticate/#credential-headers