21

I have an AngularJS WebAPI application.

As far as I can understand the OPTIONS request is constructed automatically by the browser.

POST http://localhost:3048/Token HTTP/1.1
Host: localhost:3048
Connection: keep-alive
Content-Length: 78
Accept: application/json, text/plain, */*
Origin: http://localhost:2757
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:2757/Auth/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

grant_type=password&username=xxx%40live.com&password=xxx

Response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 971
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: *
Set-Cookie: .AspNet.Cookies=CpvxrR1gPFNs0vP8GAmcUt0EiKuEzLS1stLl-70O93wsipJkLUZuNdwC8tZc5M0o1ifoCjvnRXKjEBk3nLRbFlbldJLydW2BWonr5JmBjRjXZyKtcc29ggAVhZlc2E-3gGDlyoZLAa5Et8zrAokl8vsSoXmHnsjrxZw0VecB_Ry98Ln84UuKdeHlwSBnfaKKJfsN-u3Rsm6MoEfBO5aAFEekhVBWytrYDx5ks-iVok3TjJgaPc5ex53kp7qrtH3izbjT7HtnrsYYtcfPtmsxbCXBkX4ssCBthIl-NsN2wObyoEqHMpFEf1E9sB86PJhTCySEJoeUJ5u3juTnPlQnHsk1UTcO0tDb39g-_BD-I4FWS5GMwxLNtmut3Ynjir0GndwqsvpEsLls1Y4Pq7UuVCTn7DMO4seb64Sy8oEYkKZYk9tU4tsJuGD2CAIhdSc-lAmTAA78J5NOx23klkiuSe_SSiiZo5uRpas_1CFHjhi1c8ItEMpgeTsvgTkxafq5EOIWKPRxEHbCE8Dv106k5GlKK5BaH6z7ESg5BHPBvY8; path=/; HttpOnly
X-SourceFiles: =?UTF-8?B?QzpcR1xhYmlsaXRlc3Qtc2VydmVyXFdlYlJvbGVcVG9rZW4=?=
X-Powered-By: ASP.NET
Date: Tue, 13 Jan 2015 04:54:55 GMT

{"access_token":"TkJ2trqT ....

Now logged in

I log out which is nothing more than removing the token and log in again. Something happens that is different. Before it did not send the OPTIONS but now it does. Is there something resulting from a previous request/response that would influence the browser to act different the second time I log in?

OPTIONS http://localhost:3048/Token HTTP/1.1
Host: localhost:3048
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:2757
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Access-Control-Request-Headers: accept, authorization, content-type
Accept: */*
Referer: http://localhost:2757/Auth/login
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

Response:

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 34
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?QzpcR1xhYmlsaXRlc3Qtc2VydmVyXFdlYlJvbGVcVG9rZW4=?=
X-Powered-By: ASP.NET
Date: Tue, 13 Jan 2015 04:56:32 GMT

{"error":"unsupported_grant_type"}

If I do a browser reset and reload of the page then it goes back to like before where it does not send OPTIONS the first time and I am able to log in.

Probably I need to change something on the server so it handles options.

BUT why does my browser (Chrome) not send OPTIONS the first time?

Samantha J T Star
  • 30,952
  • 84
  • 245
  • 427
  • Is it AJAX or are there any cross domain requests? – Dilip Jan 13 '15 at 05:12
  • I wouldn't worry about it. It's sending options because you are using different ports for the angular app to the api 3048 and 2757. Options are being sent due to Cors. – Wayne Ellery Jan 13 '15 at 05:16
  • 1
    But then only the second request sending OPTIONS. Why is that? – Dilip Jan 13 '15 at 05:22
  • @Wayne - Yes its a cross domain request. But I am confused. Why is it that the first time it sends no OPTIONS and then the subsequent call it sends an OPTIONS. My code and the call is same. I am trying to get the sign in code working and wondering why it works first time but does not the second time. I realize I have to address why it does not 2nd time but why the different behaviour from the browser? – Samantha J T Star Jan 13 '15 at 05:23
  • @Dilip - Yes that's what I am confused about. First time it does not send OPTIONS. – Samantha J T Star Jan 13 '15 at 05:25
  • Aren't you missing the first request. Wouldn't there be one before the post? – Wayne Ellery Jan 13 '15 at 05:27
  • There is nothing at all. I am using Fiddler and I can see everything. No OPTIONS before the first POST. I assume it's the way Chrome is working. Also after a full browser page reset. The browser doesn't do the first OPTIONS. – Samantha J T Star Jan 13 '15 at 05:30
  • @SamanthaJ - Did you have a look at http://stackoverflow.com/a/1320708/1188938 ? – Dilip Jan 13 '15 at 05:34
  • @Dilip - Thanks but still does not explain why Chrome does not send the OPTIONS the first time. – Samantha J T Star Jan 13 '15 at 05:39
  • @Dilip - I notice same with Firefox. First time no OPTIONS, second time sends OPTIONS. – Samantha J T Star Jan 13 '15 at 05:42
  • Possible duplicate of [Why am I getting an OPTIONS request instead of a GET request?](https://stackoverflow.com/questions/1256593/why-am-i-getting-an-options-request-instead-of-a-get-request) – jjbskir Dec 07 '18 at 19:34

3 Answers3

30

Whether the Chrome (or any other browser) sends an OPTIONS request is exactly specified by the CORS specfication:

When the cross-origin request algorithm is invoked, these steps must be followed:
...
2. If the following conditions are true, follow the simple cross-origin request algorithm:

3. Otherwise, follow the cross-origin request with preflight algorithm.
Note: Cross-origin requests using a method that is simple with author request headers that are not simple will have a preflight request to ensure that the resource can handle those headers. (Similarly to requests using a method that is not a simple method.)

Your OPTIONS request contains the following request header:

Access-Control-Request-Headers: accept, authorization, content-type

This means that your Angular app has inserted the non-simple Authorization request header, probably as a part of an authentication scheme. Non-simple "author request headers" trigger the OPTIONS request, as you can see in the above quote.

To allow the request to succeed, your server should handle OPTIONS request and respond with:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Headers: authorization

To learn more about CORS, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • those `author-request-headers` links don't show anything about that - I can't find the term at all in the linked docs - I think CORS was moved out of w3c specs. I can still find what I think is the doc you're referring, at https://www.w3.org/TR/2020/SPSD-cors-20200602/#author-request-headers – Steve Campbell Nov 30 '20 at 17:40
1

When you first login you most likely set the Authorization HTTP header somewhere in your login procedure. On the other side, you forgot to remove this header when the user logs out.

When you try to login again, the Authorization HTTP header is still present. This triggers the browser to perform a preflight request (see explanation of Rob W: https://stackoverflow.com/a/27924344/548020. Considering that you try to login with a grant type password it does not make sense to send an Authorization header, as this implies that you are already authorized (= logged in). Your are basically asking your backend to log you in and at the same time telling your backend that you are already authorized (= logged in).

This can be fixed by simple removing the Authorization HTTP header when the user logs out.

CodeZombie
  • 5,367
  • 3
  • 30
  • 37
0

You can also clean your Headers when you login, before sending your POST request:

delete $http.defaults.headers.common['Authorization'];