Reputation: 3235
In short, I'm posting data with Angular to a Laravel backend. The OPTIONS/preflight request looks good, but the subsequent POST fails saying that Access-Control-Allow-Origin header is missing from the requested resource.
I'm using Laravel 5 with Angular 1.2.26. Some further documentation on the backend middleware can be found here: https://laracasts.com/discuss/channels/requests/laravel-5-cors-headers-with-filters.
Laravel middleware:
public function handle($request, Closure $next)
{
return $next($request)->header('Access-Control-Allow-Origin' , 'http://laravel.app:8001')
->header('Access-Control-Allow-Credentials', 'true')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With')
->header('Access-Control-Max-Age', '28800');
}
Angular config - I've tried with various combinations of the commented code, same results each time:
$httpProvider.defaults.useXDomain = true;
//$httpProvider.defaults.withCredentials = true;
//delete $httpProvider.defaults.headers.common["X-Requested-With"];
//$httpProvider.defaults.headers.common["Accept"] = "application/json";
//$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
Preflight/OPTIONS:
Remote Address:127.0.0.1:8000
Request URL:http://laravel.app:8000/api/v1/authentication/login
Request Method:OPTIONS
Status Code:200 OK
Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:laravel.app:8000
Origin:http://laravel.app:8001
Referer:http://laravel.app:8001/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36
Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Accept, Authorization, X-Requested-With
Access-Control-Allow-Methods:POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin:http://laravel.app:8001
Access-Control-Max-Age:28800
Allow:GET,HEAD,POST
Cache-Control:no-cache
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Mon, 24 Nov 2014 16:01:57 GMT
Server:nginx/1.6.2
Set-Cookie:laravel_session=blahblah; expires=Mon, 24-Nov-2014 18:01:57 GMT; Max-Age=7200; path=/; httponly
Set-Cookie:XSRF-TOKEN=blahblah; expires=Thu, 01-Jan-1970 00:02:00 GMT; Max-Age=-1416844797; path=/; httponly
Transfer-Encoding:chunked
POST:
Remote Address:127.0.0.1:8000
Request URL:http://laravel.app:8000/api/v1/authentication/login
Request Method:POST
Status Code:500 Internal Server Error
Request Headers
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:47
Content-Type:application/json;charset=UTF-8
Host:laravel.app:8000
Origin:http://laravel.app:8001
Referer:http://laravel.app:8001/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36
Request Payload
{email: "x", password: "x", rememberMe: false}
email: "x"
password: "x"
rememberMe: false
Response Headers
Cache-Control:no-cache
Connection:keep-alive
Content-Type:text/html; charset=UTF-8
Date:Mon, 24 Nov 2014 16:01:57 GMT
Server:nginx/1.6.2
Transfer-Encoding:chunked
Upvotes: 3
Views: 7582
Reputation: 1076
I had same problem. In CORS middleware Add the following header.
public function handle($request, Closure $next)
{
//All the domains you want to whitelist
$trusted_domains = ["http://localhost:4200", "http://127.0.0.1:4200", "http://localhost:3000", "http://127.0.0.1:3000"];
if (isset($request->server()['HTTP_ORIGIN'])) {
$origin = $request->server()['HTTP_ORIGIN'];
if (in_array($origin, $trusted_domains)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Headers: Origin, Content-Type, Authorization, X-Auth-Token,x-xsrf-token');
header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE');
}
}
return $next($request);
}
Upvotes: 0
Reputation: 116
I had seme problem. If you use middleware to set Headers, but create a new Response, Response::json()
, Response::make()
, etc. on controller, this new object don't get the headers set by middleware.
Upvotes: 0
Reputation: 1585
After stepping through the VerifyCsrfToken middleware I've established that it was indeed a token mismatch.
The reason is that Angular was not supplying the CSRF token via header or via a parameter in the post. It worked for GET and OPTIONS requests because these do not validate against the token.
So, I looked into Angular and there is documentation on XSRF Protection (see https://docs.angularjs.org/api/ng/service/$http) and lots of discussion out there on how to add the appropriate headers (e.g. https://github.com/angular/angular.js/issues/5122#issuecomment-36157820).
I haven't had a chance to follow any of this up as I have to keep my project moving and my particular use case allows me to get away with disabling VerifyCsrfToken as I only need CORS whilst in development.
But hopefully this will give someone else a starting point for solving this issue more fully.
Upvotes: 3
Reputation: 3235
I am unsure if this is an issue with Laravel and the VerifyCsrfToken middleware or not, but the root cause of my issue was that the CSRF token validation was failing. When the error was thrown, the new headers were not included. I'm unclear on the order that these middlewares run in, perhaps that's it, but nonetheless, once I removed the VerifyCsrfToken from the middleware stack, everything lit up.
Upvotes: 2