Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS error when calling the API from the client side with a JWT #91

Open
aditecco opened this issue Mar 11, 2021 · 11 comments
Open

CORS error when calling the API from the client side with a JWT #91

aditecco opened this issue Mar 11, 2021 · 11 comments

Comments

@aditecco
Copy link

aditecco commented Mar 11, 2021

Hi, and thanks for your work with this API!

I'm trying to build a small application and, bear with me, since I'm a total back-end newbie I'm having a hard time figuring out how to get through CORS issues.

Basically, I can correctly obtain my JWT via the claim API; but, when I use that JWT in my front-end, I always hit the CORS roadblock:

Access to XMLHttpRequest at 'https://trefle.io/api/v1/plants/search?token=<token>&q=mint' (redirected from 'http://trefle.io/api/v1/plants/search?token=<token>&q=mint') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I'm also referencing an existing comment because it describes my case:

I have the same issue but when calling the API from the client side with a fresh JWT token using Fetch:

fetch(`https://trefle.io/api/plants?q=rosemary?token=${JWT_TOKEN}`);

Results in:

Access to fetch at 'https://trefle.io/api/plants?q=rosemary?token={JWT_TOKEN}' from origin
 'http://localhost:8080' has been blocked by CORS policy: 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.

I tried fetching passing options, even with mode: 'no-cors', but returns a 401:

fetch(`https://trefle.io/api/plants?q=${query}?token=${JWT_TOKEN}`,
      {
        method: 'GET',
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
      }
 );

Originally posted by @lewislbr in #23 (comment)

I'm totally out of ideas and unable to use your awesome API. Hope you can help.

Thanks!

PS

Sorry if I'm not using the issue template, I don't think this request fits in the categories.

@aditecco
Copy link
Author

@lambda2 sorry, just a ping in case you didn't see this issue : )

@rafaeelaudibert
Copy link

You need to set up your own backend which allows CORS requests, @aditecco.

Treffle API doesn't return the required headers so that you could request it from a browser directly (for safety reasons). This way, you should create your own backend, and make your requests to your backend, which in turn will make the requests to Treffle API and respond to your frontend.

Alternatively, you could use a tool like NoCORS to do so.

@aditecco
Copy link
Author

Hey @rafaeelaudibert ,

Thanks for your help and the NoCORS suggestion.

As for the API, I was under the impression that once I had obtained a JWT I could make requests from the client. Was I mistaken? And if as you say I can query server-to-server, then why do I need a JWT?

Please excuse the naïve questions.

Anyway, just to confirm, I was able to query the API from Nextjs's node server (API routes) and it worked without issue; but I abandoned that solution because it seemed cumbersome to create methods in the back-end just to act as a bridge to query Treffle.

But, if you say there's really no way to make it work in the client, I'll restore that workaround.

Thanks,

@rafaeelaudibert
Copy link

rafaeelaudibert commented Mar 28, 2021

Hello @aditecco.

I think you should try to understand HOW core works (and maybe WHY). I will try to give a simple explanation, but as I wrote in the comment before, you can read about it here.

When you are in a browser, you are in a kind of sandboxed environment, obeying a lot of rules that a server can send. By default, if a server doesn't send an 'Access-Control-Allow-Origin' header, the request won't be fulfilled by a browser. Using these headers, the server can then choose to allow requests coming from a specific set of origins (domain origins, for example, only requested from github.com, only requests from yourwebsite.com), restricting this way that another website, for example, could make any request to your API. TrefleAPI is not returning any header related to this so it can't be used from a browser.

The reason why you can make this request from your NextJS server is that you are not bound to this restriction in your Node code, because you are handling the request directly, and there is no browser in between. This restrictions only apply to browsers, to make the internet safer and "configurable" (although that is not a good word, I must admit).

So, the intended way to use this API is to create a browser in between your website and TrefleAPI. Your server then sends a Access-Control-Allow-Origin which allows your domain and only your domain (you could allow every domain using *, but that would probably be a bad idea) to make requests to your webserver. So that you don't need to build every route you need, you can use your server only as a Proxy, which adds the token and the neccessary header. For example, you request yourserver.com?q=some_query and your server proxies the requests, returning wherever Trefle returns from trefle.api?token=<your token>&q=some_query. This is also intended so that you hide your API Key from the public, as this would be not good to be accessible by others (because they could easily use your token in your place).

Edit: Also, checking the Auth part in the documentation, you can make requests directly from the frontend, but the request to generate the token will need to be done from the backend because it doesn't return a Access-Control-Allow-Origin header (and also you need to use your token, so that's a nope).

@nsitu
Copy link

nsitu commented Apr 7, 2021

Your explanation is helpful @rafaeelaudibert, however I'm still not clear on @aditecco 's original question. Can you or can you not "make requests directly from the frontend"? The documentation speaks of "Client-Side" tokens. I have yet to see such a token working. How does one go about using a Client-Side token when all Client-Side requests are blocked?

@aditecco
Copy link
Author

aditecco commented Apr 7, 2021

Hey, sorry I didn't get back to the issue.

@nsitu Thank you, that's exactly the crux of the issue. According to the docs:

If you need to perform client-side requests, you will have to request a client-side token from you backend, and get a JWT token from this API call in return. This token will be usable on the client side. → ref:

The docs are telling us we can make client-side reqs, provided we have a JWT. So, in my app I created the logic to get the JWT from the back-end; but when I put it in my client-side requests, they are consistently blocked due to CORS. So, in fact, it appears we can't make client side reqs with Trefle.

@rafaeelaudibert Thank you for the explanation. I know that CORS prevents me from calling "foreign" hosts from the origin host, but the point of my issue is what's explained above.

At least, if the Auth section is misleading as it appears, I think it should be amended to reflect the facts.

By all means, if I'm mistaken correct me!

Thanks,

@rafaeelaudibert
Copy link

I'll try with the help of a drawing. The flow is as follows:
image

Explaining:

  • You are on your page: yourdomain.com
  • You do a request to your backend (which can be hosted anywhere, given that it allows requests incoming from yourdomain.com on their CORS configuration) asking for a token
  • Your backend sends a request to trefle.io/api/auth/claim saying from which domain you want to be able to generate client side requests with a token (in this case yourdomain.com)
  • Trefle.io generates a token for which it will allow CORS requests iff it is from yourdomain.com
  • You send this token back to the client side
  • Using this token you can do requests to Trefle GIVEN that you are on the domain you passed to the trefle.io/api/auth/claim endpoint in your backend

For example, let's se me trying. I generated a token for https://www.rafaaudibert.dev (a domain I own and can do requests from) using Trifolium library in the backend:
image
OBS: The JWT token is cut in the message so that you guys can't use, I hope u understand.

Then, on my browser, if I am on a random domain, I can't access it, because I will receive a CORS error:
image

BUT, if I am on https://www.rafaaudibert.dev I can easily request it:
image

Hope that helps @aditecco @nsitu

@nsitu
Copy link

nsitu commented Apr 7, 2021

This is enlightening and I appreciate the explanation @rafaeelaudibert, thank you. I just noticed that the JWT is passed as a Bearer token in an authorization header, whereas the original Trefle token is passed more simply as a URL parameter. I had missed this variation earlier, and I suspect it is the culprit here. I will revisit this when I have some time to experiment.

@w0244079
Copy link

w0244079 commented Apr 8, 2021

@rafaeelaudibert I'm a little confused as to why enabling CORS would be considered a "security issue" given that thousands of APIs allow it. Are those APIs all doing it wrong? Could you explain the reasoning behind this? Thank you.

@rafaeelaudibert
Copy link

@nsitu It works the same way even if you pass it through a query parameter. I just passed through a header because I find it cleaner and more spec-complaint.

@w0244079 Most of the APIs which have PRIVATE tokens won't allow you to do requests from the client side. When you build an API for your client, you allow CORS for your domain (you can specify saying that you are only accepting messages from a given domain). Outside of that, in Trefle specific case is because you want to protect the token you generated. Trefle wants to know who is using this token, to revoke it in some cases of abuse, or even to handle the rate-limiting. If you make your token freely available on the client-side, any person could simply steal it (After all, you can just watch the code) and use it without any consequences, and Trefle would think you are the person using the API for this and maybe causing some problems (think of using the token for a DDOs on Trefle servers). There are other more important security implications related to the fact that you don't want to allow people to execute requests to your server from other websites (why would they be doing that? it probably is malicious, and dubious at least), but I don't feel I'm versed enough so I found some links:

@nsitu
Copy link

nsitu commented Apr 8, 2021

@rafaeelaudibert thanks for the added clarity. I made a boilerplate example for Node.js here: https://github.com/nsitu/PlantFinder
It is working fine and I think it follows your diagram as well :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants