4

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
Community
  • 1
  • 1
caitlin
  • 2,769
  • 4
  • 29
  • 65
  • First: You should probably use the more common `Authorization` header instead of your custom `authtoken` header. Second: `Access-Control-Allow-Headers:Content-Type, x-xsrf-token`... I am not seeing that you are allowed to send your custom `authtoken` header; it's not in the allowed list. That is probably the issue. – TheSharpieOne Dec 29 '16 at 20:13
  • I would still need to have the `Access-Control-Allow-Origin: x.x.x.x` (generally an IP address) header set in the response, though. And it can't be a wildcard. The header needs to be customized because the requests are going to be sent from an app. – caitlin Dec 29 '16 at 20:19
  • `Access-Control-Allow-Origin` is not typically an IP address, it's usually domain names... unless you are accessing your site via IP (in the URL bar) it will be fully qualified domain names. `*` works fine for testing, you would want to lock it down later. But that is neither here nor there... not sure why you brought it up... I was talking about Access-Control-Allow-**Headers** which currently does not allow you to send `authtoken` header and will block your request as it is not allowed by your configuration. – TheSharpieOne Dec 29 '16 at 20:24

1 Answers1

2

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();
    }
caitlin
  • 2,769
  • 4
  • 29
  • 65
  • You may want to have a look at [**dispatcher filters**](http://book.cakephp.org/2.0/en/development/dispatch-filters.html) and [**`CakeResponse::cors()`**](http://api.cakephp.org/2.8/class-CakeResponse.html#_cors). – ndm Dec 30 '16 at 01:02