As I wrote this, it kinda turned into paraphrasing the Mozilla CORS docs. The specific answers to your questions are tagged with "Suggestion:".
A little background on CORS
CORS policy is an extension of the Same-origin policy (specifically, CORS tells the browser what to do when a bit of javascript tries to make a cross-origin call). According to Mozilla, the definition of "same origin" is:
Two URLs have the same origin if the protocol, port (if specified), and host are the same for both.
Your two URLs are:
https://api.myapp.example.com
and
https://myapp.example.com
Same protocol (https), same port (unspecified), but different host, ergo the browser considers them different origins and will invoke CORS
Suggestion: So the first thing you could do is host your UI and API on the same protocol, port, and host, and then you don't need to worry about any of this mess. But I'll continue on assuming that you can't do that for whatever reason.
What value for Access-Control-Allow-Origin
?
In this section I'm basically just quoting Mozilla's CORS page.
Here's how this is going to work: your UI's javascript is going to construct some request; probably by building up an XMLHttpRequest
with body and headers and whatever, then it's gonna go to send it and the browser will go "whoah whoah, this is a non-simple cross-origin request, I need to do CORS before I send this request. It will send a "preflight" request with the OPTIONS
HTTP verb to, basically, ask permission to send the real request. You may see your browser send a pre-flight like this:
OPTIONS /api/v1/phonenumber HTTP/1.1
Host: api.myapp.example.com
User-Agent: Mozilla/5.0
... snip ...
Origin: https://myapp.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type, X-Auth-Token
Which, in plain English means:
Hi api.myapp.example.com
, this is Firefox!
The javascript at https://myapp.example.com
is trying to send you POST /api/v1/phonenumber
and it set custom values on the following headers: {Authorization, Content-Type, X-Auth-Token}
is it cool if I let that through?
If the server returns an error or anything other than a proper CORS response, then the browser will not send the actual POST. A successful CORS response might look like this:
HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://myapp.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type, X-Auth-Token
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Broken down, that basically means:
Hi Firefox!
Yes, you can send that to me. Also, in case it sends similar requests in the future, this URL can be called by https://myapp.example.com
with GET
, POST
, PUT
and DELETE
, and with custom values for the Authorization
, Content-Type
, and X-Auth-Token
headers. You don't need to ask me again for 86400
seconds. But please do ask me again if the same request comes with a different Accept-Encoding
or Origin
, because I may give a different answer.
Suggestion: If your JS will never set a value in the X-Auth-Token
request header, then there's no need to specify it here.
Suggestion: Long story short, you want your API server to respond to OPTIONS requests with:
Access-Control-Allow-Origin: https://myapp.example.com
Vary: Origin
In your case, you can probably just hard-code that. For other readers, since you can't put a list here, the more general method is to look at the Origin:
on the OPTIONS req, decide if that meets your rule or not, and if so then echo it back.
Access-Control-Expose-Headers
This tells the browser that javascript is allowed to read the specified headers. It sounds like you're returning your JWT in the response body, so you shouldn't need to set this, but if you were returning the JWT in, for example, an Authorization header that the JS needs to read, then you would need to specify that here.
Suggestion: If your server will never set a value in the X-Auth-Token
response header, then there's no need to specify it here.
I think / hope my ramble has covered all your questions.
PS. CORS is stupidly complicated. As one of my colleagues recently said "The more I learn about CORS, the less I understand it". I am definitely still a student when it comes to CORS.