Reputation: 2819
I'm building an Ionic 1 app that talks to a REST API implemented in CakePHP 2.8, using JSON Web Tokens (JWT) for auth.
In an unauthorized state, my app is able to make GET / POST requests to the server with no issue. However, once I have authenticated and my app is sending a authToken
header along with every request, Angular automatically sends an OPTIONS
preflight request first.
This is where the problem starts. Because the automatic preflight request does not have the authToken
header set, and because the API endpoint requires authorization, CakePHP responds with a 302 FOUND redirect to /login
. The app (or browser, at this testing phase) considers this unsafe, and never goes on to make the proper request.
My question: How can I get CakePHP to respond appropriately to preflight OPTIONS requests so that AngularJS knows it can safely send a custom header to a cross-domain server?
Per this question, this is what needs to happen:
Request headers:
Origin: http://yourdomain.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header
Response headers:
Access-Control-Allow-Origin: http://yourdomain.com // Same as origin above
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-Custom-Header
Things I've tried:
-Setting .htaccess
to always respond to OPTIONS
requests with a 200
. I get a 200 response, but the response says "your server is misconfigured" and, because it doesn't have the proper headers set, the real $http
request never goes through.
-Getting CakePHP to always respond a certain way to OPTIONS
requests. The issue here is getting Cake to skip authorization, skip trying to run the controller action, and send back a HTTP 200 response with the proper headers set.
// AppController::beforeFilter();
if($this->request->is("options")){
// Send the appropriate response based on the request
}
For the curious, here are the headers that get exchanged in the course of a failed $http
request:
GENERAL:
Request URL:https://api.example.com/rest_users/profile.json
Request Method:OPTIONS
Status Code:302 Found
Remote Address:x.x.x.x:443
REQUEST HEADERS:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US
Access-Control-Request-Headers:authtoken
Access-Control-Request-Method:GET
Cache-Control:no-cache
Connection:keep-alive
DNT:1
Host:api.example.com
Origin:http://x.x.x.x:8100
Pragma:no-cache
Referer:http://x.x.x.x:8100/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
RESPONSE HEADERS:
Access-Control-Allow-Headers:Content-Type, x-xsrf-token
Access-Control-Allow-Methods:*
Access-Control-Allow-Origin:*
Access-Control-Max-Age:172800
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:20
Content-Type:text/html; charset=UTF-8
Date:Thu, 29 Dec 2016 18:51:30 GMT
Keep-Alive:timeout=15, max=99
Location:https://api.example.com/login
Server:Apache/2.2.15 (CentOS)
Vary:Accept-Encoding
X-Powered-By:PHP/5.3.3
Upvotes: 4
Views: 1937
Reputation: 2819
I found a solution that allows CakePHP to handle CORS preflights correctly. It sets the headers, sends the response, and shuts down Cake before the requested action can run.
Be sure to have parent::beforeFilter();
in all your controllers so that this code runs.
In AppController::beforeFilter():
if($this->request->is("options")){
// Set the headers
$this->response->header('Access-Control-Allow-Origin','*');
$this->response->header('Access-Control-Allow-Methods','*');
$this->response->header('Access-Control-Allow-Headers','Content-Type, Authorization');
// Send the response
$this->response->send();
// Don't do anything else!
$this->_stop();
}
Upvotes: 2