My web page connected to iNaturalist fails in Chrome: CORS preflight OPTIONS does not return support to POST

I’m trying to build a sample web page that uploads an observation and a picture. I use chrome to test it.

You can test it at

To test it you only have to [Log in in iNaturalist], select a picture (a file) and press [Send a test observation]. Internally it executes some AJAX GET requests and an AJAX POST observations and a POST observation_photos. I creates an observation with a photo.

The problem is simple to describe:

  • any GET request has no CORS issues and I’m able to read the json data that I get back. GREAT!.
  • a POST request to observations.json is still considered a “simple request” and does not require a preflight OPERATIONS request so it is send and accepted by the server. Unfortunately I’m not able to read the json response but this is not big deal because if there is no mistake in the request I can assume it will create the observation (actually it does that) and wait for a bit an do a GET request to get back some data that confirms that the observation has been stored.
  • a POST request that sends a “file” (the image) fails because Chrome believes a “preflight request” is necessary. DEAD END!.

I have tested the OPERATIONS request in Postman and I have demonstrated that the OPERATIONS responds ERRONEOUSLY that Access-Control-Allow-Methods is GET, OPTIONS only. I cannot manage to get a correct response saying that also POST, PUT and DELETE are supported. I have tried almost everything!.

This is very frustrating because I was almost ready to complete my task but I do not know how to overcome this issue without having to add my own server in between. I do not want to do this if it is possible.

I have hopes that it is not my mistake and that the responsible people taking care of the iNaturalist servers can fix this in their end following something similar to this instructions:

Thanks for your help.

I don’t fully understand the problem but have you tried it with https instead of http? Also, you might think about disabling the default overscroll response because it’s very annoying to accidentally refresh a page after you’ve filled in a form!

Thanks @ourmikegeorge for your response. Thanks for spotting my mistake. it is definitely necessary to use https in the test example. I corrected the example URL in my original question. I do not understand the second part of your response.

when you say “actually it does that”, are you saying that you’ve created observations using this app? if so, can you point to one that was created with your app? (or if one doesn’t exist right now, would you create one and point to it as a proof that that works? if it’s been created with your app, i would expect to see that its application_id = your app id.)

it looks like you’re one of a handful of people who have tried to use PKCE flow for your app. when i did it, i initially ran into something that appeared similar to what you’re describing with CORS messages, and in my case, what was actually happening there was that i hadn’t gotten the bearer token or applied it correctly or something like that. so iNaturalist was redirecting me to the login page, i think, or something like that, and it wasn’t obvious to me that that was what it was doing. (but after i fixed the problems with the token handling, it worked just fine.)

i’ve briefly scanned your code, though i haven’t actually run your app (because i didn’t want to grant privileges to code that was opaque to me, since i couldn’t see what was going on in the minimized hello.js code). i was trying to figure out if the PKCE flow was set up correctly, but i couldn’t tell (with the minimized code). i also thought maybe there was an issue with scopes, and again, i couldn’t tell, though i did see that you have scopes defined in your hello.login call and also in your inaturalist.js file, though i’m not sure how they interact with each other if at all… so i guess what i’m saying is i couldn’t tell what was going on in your code without a lot more effort.

also, it looks like you’re hitting the original Ruby version of the API. why not hit the more recent Node version of the API? (you could also consider using iNaturalist’s Javascript package, if you’re going to be doing a lot of coding:

1 Like

Thanks @pisum for your answer.
First I understand your concerns on the hello.js library. It happens that I learned to use it in another project not related to iNaturalist (adodson . com / hello.js) and I’m confident it is save.

Anyway. Thanks for suggesting about using api . inaturalist . org / v1 / docs /
First things first: great news IT WORKS!!!. I sooooo happy!.


1.- I’m not able to formulate GET operations that require authentication. They return 401 - E.g. / users / me Workaround: Use the old endpoint API for GET: / users / edit.json
(I need this to know the name of the person that has authenticate and say “Hello” to her).

2.- POST /observations seems to work but I get back an empty response. In practice this means that I’m not getting back the id of the observation created It returns 200 and no CORS issues anymore but a empty body. Workaround: Use the old endpoint API for GET: / observations / {username}.json ? updated_since={recent time} and I extract the id for the first observation in the response.

3.- POST /observation_photos works great with a full response. No CORS issues.

Any suggestions on my two issues? I do not like workarounds but they are better than nothing!.

Example of an observation created with the test URL (joanma . uab . cat / projectes / RitmeNatura / captura /): www . inaturalist . org / observations / 38844214

you could also consider using iNaturalist’s Javascript package

I had no idea it existed. Actually it looks very nice. But as I said, I’m almost there with my code so no need to change everything at this point.

hmmm… i’m not certain i fully understand what you’re noting above, but i will say that my understanding is that Node API endpoints that require authentication use a JSON Web Token (JWT) that is different from the token you get at the end of the OAuth process. you can get a JWT (good for 24 hours) by using the OAuth token and hitting (then you can use the JWT to hit a Node API endpoint like users/me.) hopefully that gets you further in the right direction. if you still are encountering issues after that, let me know what remains as issues, and i might be able to help address those then.

If you view the page on a mobile browser and you swipe down when you’re already at the top of the page, it refreshes and you lose what you’ve entered in the form. You can prevent this happening by adding

overscroll-behavior: none;

to the body element in your css.

Thanks @pisum for your clear instructions. I believe I understand the steps that I need to take to make it work based on your indications. It is surprising that some POST operations in the NodeJS API work with a bearer token even if a api token is the one required. I have tested the POST /observations and the GET /users/me and with the api token and both seems to work and respond the requested data. GREAT!. I’ll try to integrate the suggested solution in my application tomorrow and demonstrate how to do it right (hopefully).
Thanks for your useful feedback.

@ ourmikegeorge I see what you mean. I’ll test the overscroll-behavior to learn about it.

1 Like

yeah… that’s what i thought you were noting in your previous reply, and i thought that that surely couldn’t be the case, could it? but if you’re saying it’s doing that, it sounds like a potentially really bad thing. (it makes me think that maybe if you passed in even an invalid token that maybe the Node API would still execute your POST?) i don’t have time to poke into this myself, but @tiwane, @kueda, or other staff may be able to provide some insight into why this behaves this way, if it is indeed working as described.

you’re welcome. i’m happy to help, and i’m glad to see others working on interesting iNaturalist API projects.

1 Like

Some endpoints at pass requests through to, and for those an oauth token will work (though not an invalid one). It’s not something I recommend doing though. Better to get a fresh JWT and use it for all requests to that require auth.

@joanma747, do you have your code posted somewhere in an unminimized form?


i think only the hello.js code is minimized. based on joanma747’s notes above, it looks like you can get the unminimized code for that here:

if you’re curious about what i was curious about, then, well, hmmm…

Thanks @pisum and @kueda.

Some endpoints at pass requests through to, and for those an oauth token will work (though not an invalid one).
That will explain it!

Yes, my code is not minimizend (only the library used to authenticate is (hello.js)) and tries to be compact and nice but… I’m saving it in the current status here https :// / projectes / RitmeNatura / captura / tokens.htm
I’ll try now to edit the previous code to apply the api_token solution in it https :// / projectes / RitmeNatura / captura / and get back to you with the results.


I can confirm that the suggested modifications works fine and my testing web page is working fine now:

Still, there is an small unexpected problem to consider:
I’m creating a observation using POST /observations and I get back the observation_id in the response. Even if I get back the id, that does not mean that the observations is fully created. If immediately after creating the observation I send a POST observation_photos, about 60% of the times I’ll get a HTTP 422 with a “observations was not created” message. I have fixed this by introducing a 2 second delay between both POST calls.

Now the testing app is working just fine and I’ll proceed to integrate it in our acquisition form. It is nice when things work!!. Thanks for your support

1 Like

@joanma747 – i was trying to figure out how hello.js does its PKCE flow, since its documentation says it handles explicit flow only through a proxy server and otherwise uses implicit… i’m still scratching my head because it doesn’t look like it is doing PKCE… :thinking:

are you having to use the proxy server service – maybe in conjunction with your nonce? – to get the authorization flow to work?

edit: oh… maybe you’re actually using a regular explicit flow, passing in the app secret using a proxy, and i just thought you were using pkce because your app is set up as confidential=false (but maybe even when confidential=false, you can still use a regular authorization flow).

I’m not an expert in authorization. I have a friend that is expert and he told me to use the hello.js library for another project. I was copying the methodology for the iNaturalist case. The tricky part was to create the iNaturalist.js file to get it working. I was just following examples not knowing much about what I was doing. At some point there was a message requesting to register something in I did that and afterwords all worked just fine. I believe this might be the proxy server that you are looking for.
Sorry for not being able to be more clear this time.

1 Like

ok… yes, that confirms my suspicions that you’re relying on a proxy server.

my only concern is how you set up your registration on that proxy. if you set it up with a client secret that you got from your iNaturalist app registration (which is probably the case), then that’s probably ok, although your iNaturalist app registration probably should reflect confidential=true rather than confidential=false (since that would mean you’re not using PKCE flow).

if on the other hand you’ve set up your proxy server with a code validator value that matches a hard-coded value stored in your iNaturalist.js or index.html files, then that may be a problem because that probably means the PKCE flow is not being set up properly.

i bet this thread can be reclassified as a general issue rather than a bug report, since it wasn’t really a bug. it can also probably be closed, i bet.

@joanma747 – any objections to closing this thread? (i still have suspicions that your authorization flow wasn’t quite right, but it seems you’ve probably already moved past it at this point.)