I have my backend(Ruby On Rails) running at port number 3000 with login endpoint(http://localhost:3000/login).I also have angular server running on port 4200.So with angular I am hitting login endpoint and upon successful login, response is sent back with Set-cookie option(authentication token is set).But it is not set in the browser.Below is the response header i am receiving:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:http://localhost:4200
Access-Control-Max-Age:1728000
Cache-Control:max-age=0, private, must-revalidate
Content-Type:application/json; charset=utf-8
ETag:W/"9dae161444bc908fd3289f4ed0fb15ed"
Set-Cookie:auth_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJleHAiOjE1MDU4NDM0Nzh9.0E22wlacZWvmazpdJPLC4oRq3QM-8DX-npQu3w-U8Vs; path=/
Transfer-Encoding:chunked
Vary:Origin
X-Request-Id:267cfe1f-e60c-40b5-9a7b-4801d358cfd4
X-Runtime:0.273751
Request Headers
view source
While if I hit that API with Postman then the cookie is saved without problem.
Related
I think I understand CORS pretty well, but I'm still a bit puzzled about the browser's behavior when it comes to the preflight requests.
Let's say the browser issues this preflight request:
OPTIONS http://myserver.local:7000/api/order/4 HTTP/1.1
Host: myserver.local:7000
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-my-custom-header
Origin: http://localhost:5000
Sec-Fetch-Mode: cors
Referer: http://localhost:5000/
and my API returns:
HTTP/1.1 204 No Content
Date: Wed, 09 Mar 2022 12:52:50 GMT
Server: Kestrel
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT,DELETE
Access-Control-Allow-Origin: http://localhost:5000
Vary: Origin
Note that the server allows methods PUT and DELETE in the response to the preflight request, but not POST, which is the method of the actual CORS request.
Should the browser not block this request due to the mismatch between the actual request's method and the methods listed in the Access-Control-Allow-Methods header? Or is it enough that the server respond with a 20x status code for the browser to accept the preflight and then send the actual request?
My assumptions was that the browser would compare the allow-methods and block the request if the requested method did no match... Am I missing something?
TL;DR
No, the browser doesn't require the server to explicitly allow the POST method, because the latter, as a so-called CORS-safelisted method, gets a free pass.
More details
What the spec says
The answer, as always, lies in the Fetch standard (section 4.8), which specifies how CORS works:
Let methods be the result of extracting header list values given Access-Control-Allow-Methods and response’s header list.
And further down:
If request’s method is not in methods, request’s method is not a CORS-safelisted method, and request’s credentials mode is "include" or methods does not contain *, then return a network error.
(my emphasis)
What is a CORS-safelisted method? The term is defined in section 2.2.1:
A CORS-safelisted method is a method that is GET, HEAD, or POST.
Interpretation
If the method of the CORS request is one of GET, HEAD, or POST, the browser doesn't require the server to explicitly list that method in the Access-Control-Allow-Methods header for CORS preflight to succeed.
Experiment
I've found Jake Archibald's CORS playground useful for testing my (mis)understanding of CORS. Running this particular instance in your browser may convince you that the POST method doesn't need to be explicitly allowed for CORS preflight to succeed.
I have two DotNet MVC sites. One accesses a Web API from the other with an AJAX GET call.
This all worked, but has stopped functioning now. I've hardly made any changes on my side, so I'm wondering if my host might have made changes (in IIS, for example) that would stop this from working?
Here's how I initially got it working...
I installed the Microsoft.Aspnet.Cors and Microsoft.Aspnet.WebApi.Cors packages.
I added the following code...
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
And in the controller for my API I added...
namespace Webscope.Controllers
{
[EnableCors(origins: "[URL of my other website]", headers: "*", methods: "*")]
public class EventAPIController : ApiController
This used to work, but now get the following error in the console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https:[my website URL]/EventRead/1-1-2015/12-12-2099. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
In response to #FoggyDay's answer below, I've called the API from Fiddler and got the following headers...
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Frame-Options: AllowAll
X-Powered-By: ASP.NET
Date: Fri, 13 Mar 2020 03:56:39 GMT
Content-Length: 198
So it looks as if CORS headers have not been included in the response. Can anyone tell me why this would be?
UPDATE
I found some extraneous code from a previous attempt to get CORS working. Now that I've removed this code, I am seeing the CORS headers in Fiddler.
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: https://[ calling website's URL ]/
However I'm still getting the following error in my calling site's console...
Access to XMLHttpRequest at
'https://[ destination site URL ]/api/EventRead/1-1-2015/12-12-2099' from
origin '[ calling website's URL ]' 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.
SUGGESTIONS:
Back out your "new" changes. It sounds like you've inadvertantly introduced a second header.
Read this: Reason: CORS header 'Access-Control-Allow-Origin' missing
Look at your HTTP traffic, for example in Fiddler. Verify that you're sending the header ... and verify that you're allowing the correct combination of host and port.
If you're still having problems, post back with the exact error message and relevant HTTP headers.
I have an action that generates a password reset link and emails it to the user
public ActionResult SendResetPasswordEmail(string userName)
{
var webUser = LoadUser(userName);
if (webUser != null)
{
var token = WebSecurity.GeneratePasswordResetToken(webUser.UserName);
emailSender.SendPasswordResetEmail(webUser, token, resetAction);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "No user found with username: " + userName);
}
The first time I call the action from the browser, I get an HTTP 200 response (and hit my breakpoint in the action).
The second time I call the action from the browser, I get an HTTP 304 response indicating that the content is unchanged.
There are no [OutputCache] attributes anywhere in the source file (not on the class or the action).
What is causing the web server to decide that the content is unchanged and return the HTTP 304?
I'm aware of a work-around
https://stackoverflow.com/a/18620970/141172
I'm interested in understanding the root cause for the HTTP 304 response.
Update
Headers on first request:
Request Headers
Request GET /Companies/SendResetPasswordEmail/?userName=ej HTTP/1.1
X-Requested-With XMLHttpRequest
Accept */*
Referer http://local:6797/Companies
Accept-Language en-US
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host localhost:6797
DNT 1
Connection Keep-Alive
Cookie __RequestVerificationToken=sNOBS6qz32LtnJpLWgHHELhaE44DfIVE1LSMUgjzHjcwsvxlUFa4lOSyA5QeB8keLXYL08Psjg29CRI7W73uHLJy6A81; .ASPXAUTH=DAF8AF47E955F723EE9438866BE1B4BFBF91BA01912EF087824F03581DBCA05A4AECA01373FAF40DF0C4D5C17F17DEFA2F85C1B702988B7E0F750BFE19566FC711C7D6BD81D8F0B0ABD68AF5B3D9BA032286361F; ASP.NET_SessionId=5e2gcvkc2p3rji25z5emyqzd; HelixPlugins1.0=IEPlugin1.0
Response Headers
Response HTTP/1.1 200 OK
Server ASP.NET Development Server/11.0.0.0
Date Thu, 03 Apr 2014 23:29:02 GMT
Cache-Control private, s-maxage=0
Content-Length 0
Connection Close
NOTE: I changed localhost to local in the above because StackOverflow does not allow links containing localhost to be posted :-)
The browser is Internet Explorer 10.
IE caches ajax responses by default, you need to explicitly tell it not to do any ajax caching by setting your ajax object's cache property to false.
Browsers such as Chrome automatically append a random token to your request to make it unique.
I'm using the GTMOAuth-2 library to implement an OAuth 2 Authorization Code Grant flow to the Asana API, but consistently getting back a server error 500. The GTMHTTPFetcher log is as follows (selectively redacted):
fetch tokens for app.asana.com
2013-05-08 16:46:58 +0000
Request: POST https://app.asana.com/-/oauth_authorize
Request headers:
Content-Type: application/x-www-form-urlencoded
User-Agent: gtm-oauth2 <user-agent>
Request body: (199 bytes)
client_id=<client-id>&client_secret=_snip_&code=<client-secret>&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.google.com%2FOAuthCallback
Response: status 500
Response headers:
Cache-Control: no-store
Content-Length: 303
Content-Type: text/html; charset=UTF-8
Date: Wed, 08 May 2013 16:46:56 GMT
Pragma: no-cache
Server: nginx
Set-Cookie: <cookie>
X-Asana-Content-String-Length: 303
X-Asana-Preferred-Release-Revision: 20130508_073846_310cafc985fd5fb43121784b58d5dcd2503ffffe
Response body: (303 bytes)
<html>
<head>
<title>Error</title><script>__FILE__="(none)";var config = {
"CLUSTER": "prod",
"PRETTY_JS_CODEGEN": false,
"ENABLED_FEATURES": ""
};</script><link rel="shortcut icon" href="/-/static/luna/browser/images/favicon.ico" />
</head>
<body>
<h3>Error</h3><pre>Server Error</pre>
</body>
</html>
I've double-checked the auth and token URLs, client ID and secret, made sure the redirect URIs match on Asana and in-app. Interestingly, the authorization flow seems to get as far as authorizing the app (and the Asana site records the app as authorized), but it then never seems to return the authorization token. Is there anywhere I might be going wrong, or is this truly an internal server error?
(I work at Asana). I'm unsure where in the flow this request is being made, but it doesn't look correct. Once you've obtained the authorization code (by having the user interact with the Asana form), the library should then make a request to our /-/oauth_token endpoint, and pass us the code. So it's possible that you just need to be using the /-/oauth_token endpoint instead of the /-/oauth_authorize endpoint as you're doing.
If you look at our OAuth examples you'll note that there are two different URLs, one for authorization and one for token exchange.
Asana is probably returning a 500 because it's not expecting this and we're not doing a good enough job catching the problem.
I don't know what I do wrong, but everytime I tried to obtain the token (after user authentication of course), the result is always Invalid grant_type parameter or parameter missing
Possibly related to Box API always returns invalid grant_type parameter on obtaining access token
Here is my fiddler result:
POST https://api.box.com/oauth2/token HTTP/1.1
Host: api.box.com
Content-Length: 157
Expect: 100-continue
Connection: Keep-Alive
grant_type=authorization_code&code=nnqtYcoik7cjtHQYyn3Af8uk4LG3rYYh&client_id=[myclientId]&client_secret=[mysecret]
Result:
HTTP/1.1 400 Bad Request
Server: nginx
Date: Thu, 07 Mar 2013 11:18:36 GMT
Content-Type: application/json
Connection: keep-alive
Set-Cookie: box_visitor_id=5138778bf12a01.27393131; expires=Fri, 07-Mar-2014 11:18:35 GMT; path=/; domain=.box.com
Set-Cookie: country_code=US; expires=Mon, 06-May-2013 11:18:36 GMT; path=/
Cache-Control: no-store
Content-Length: 99
{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}
Even following the curl example gives the same error. Any help would be appreciated.
Edit: tried with additional redirect_uri params but still the same error
POST https://api.box.com/oauth2/token HTTP/1.1
Content-Type: application/json; charset=UTF-8
Host: api.box.com
Content-Length: 187
Expect: 100-continue
Connection: Keep-Alive
grant_type=authorization_code&code=R3JxS7UPm8Gjc0y7YLj9qxifdzBYzLOZ&client_id=*****&client_secret=*****&redirect_uri=http://localhost
Result:
HTTP/1.1 400 Bad Request
Server: nginx
Date: Sat, 09 Mar 2013 00:46:38 GMT
Content-Type: application/json
Connection: keep-alive
Set-Cookie: box_visitor_id=513a866ec5cfe0.48604831; expires=Sun, 09-Mar-2014 00:46:38 GMT; path=/; domain=.box.com
Set-Cookie: country_code=US; expires=Wed, 08-May-2013 00:46:38 GMT; path=/
Cache-Control: no-store
Content-Length: 99
{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}
Looks like Box requires a correct Content-Type: application/x-www-form-urlencoded request header in addition to properly URL encoding the parameters. The same seems to apply to refresh and revoke requests.
Also, per RFC 6749, the redirect_uri is only
REQUIRED, if the "redirect_uri" parameter was included in the authorization request
as described in Section 4.1.1, and their values MUST be identical.
I was facing a similar issue.
The problem is not with Content-Type.
The issue is with the lifecycle of code you receive.
One key aspect not mentioned in most places is that the code you get on redirect lasts only 30 seconds.
To get the access token and refresh token, you have to make the post request in 30 seconds or less.
If you fail to do that, you get the stated error. I found the info here.
Below code worked for me. Keep in mind, the 30-second rule.
import requests
url = 'https://api.box.com/oauth2/token'
data = [
('grant_type', 'authorization_code'),
('client_id', 'YOUR_CLIENT_ID'),
('client_secret', 'YOUR_CLIENT_SECRET'),
('code', 'XXXXXX'),
]
response = requests.post(url, data=data)
print(response.content)
Hope that helps.
You are missing the redirect URI parameter. Try:
POST https://api.box.com/oauth2/token HTTP/1.1
Host: api.box.com
Content-Length: 157
Expect: 100-continue
Connection: Keep-Alive
grant_type=authorization_code&code=nnqtYcoik7cjtHQYyn3Af8uk4LG3rYYh&client_id=[myclientId]&client_secret=[mysecret]&redirect_uri=[your-redirect-uri]
I have also face same issue implementing oauth2. I have add Content-Type: application/x-www-form-urlencoded. When I add content-type my issue solved.
Check and add valid content-type.
Not sure who might need this in the future but be sure you're sending a POST request to get the access token and not trying to retrieve it by using GET or if you're testing- pasting in the address bar won't work, you need to send a POST request with the data in the BODY and not as query parameter.
Also the code usually lasts for a few seconds, so you need to use it as soon as its sent back.