zf-fr / zfr-cors

A Zend Framework 2 module that helps you to deal with Cross-Origin Resource Sharing (CORS)
MIT License
60 stars 39 forks source link

Apigility CORS without zfr-cors - simplier is better? #24

Closed ivanvujisic closed 10 years ago

ivanvujisic commented 10 years ago

OK, I get stuck with zfr-cors installation, so I get mad and put this:

header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Authorization, WWW-Authenticate, Origin, X-Requested-With, Content-Type, Accept');

in Apigility /public/index.php, above this line:

Zend\Mvc\Application::init($appConfig)->run();

So, it "seems" to me I resolved CORS issue with Apigility (confirmed by AngularJS front end developer), but I'm sure you guys have some comments on this.

Thanks.

bakura10 commented 10 years ago

What kind of error are you getting with setting ZfrCors ? Can you show me your ZfrCors config?

ivanvujisic commented 10 years ago

Sure np, but my question is - isn't it simplier with those three header lines above?

config/application.config.php

<?php
/**
 * Configuration file generated by ZF Apigility Admin
 *
 * The previous config file has been stored in application.config.old
 */
return array(
    'modules' => array(
        'Application',
        'ZF\\DevelopmentMode',
        'ZF\\Apigility',
        'ZF\\Apigility\\Provider',
        'ZF\\Apigility\\Documentation',
        'AssetManager',
        'ZF\\ApiProblem',
        'ZF\\Configuration',
        'ZF\\MvcAuth',
        'ZF\\OAuth2',
        'ZF\\Hal',
        'ZF\\ContentNegotiation',
        'ZF\\ContentValidation',
        'ZF\\Rest',
        'ZF\\Rpc',
        'ZF\\Versioning',
        'nuagetel',
        'ZfrCors'

    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor'
        ),
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php'
        )
    )
);

Apache error_log

[Thu Jun 26 00:48:11 2014] [error] [client 212.178.238.122] PHP Fatal error:  Uncaught exception 'Zend\\ModuleManager\\Exception\\RuntimeException' with message 'Module (ZfrCors) could not be initialized.' in /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/library/Zend/ModuleManager/ModuleManager.php:189\nStack trace:\n#0 /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/library/Zend/ModuleManager/ModuleManager.php(163): Zend\\ModuleManager\\ModuleManager->loadModuleByName(Object(Zend\\ModuleManager\\ModuleEvent))\n#1 /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/library/Zend/ModuleManager/ModuleManager.php(90): Zend\\ModuleManager\\ModuleManager->loadModule('ZfrCors')\n#2 [internal function]: Zend\\ModuleManager\\ModuleManager->onLoadModules(Object(Zend\\ModuleManager\\ModuleEvent))\n#3 /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(468): call_user_func(Array, Object(Zend\\ModuleManager\\ModuleEvent))\n#4 /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/l in /var/www/html/api-zf2-02/apigility/vendor/zendframework/zendframework/library/Zend/ModuleManager/ModuleManager.php on line 189
bakura10 commented 10 years ago

Did you copy-paste the .dist file? Please read the installation part: https://github.com/zf-fr/zfr-cors#installation

ivanvujisic commented 10 years ago

Yes, I put

<?php

/**
 * This is the config file for ZfrCors. Just drop this file into your config/autoload folder (don't
 * forget to remove the .dist extension from the file), and configure it as you want
 */

return array(
    'zfr_cors' => array(
         /**
          * Set the list of allowed origins domain with protocol.
          */
         'allowed_origins' => array('*'),

         /**
          * Set the list of HTTP verbs.
          */
         'allowed_methods' => array('GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'),

         /**
          * Set the list of headers. This is returned in the preflight request to indicate
          * which HTTP headers can be used when making the actual request
          */
         'allowed_headers' => array('Authorization'),

         /**
          * Set the max age of the preflight request in seconds. A non-zero max age means
          * that the preflight will be cached during this amount of time
          */
         // 'max_age' => 120,

         /**
          * Set the list of exposed headers. This is a whitelist that authorize the browser
          * to access to some headers using the getResponseHeader() JavaScript method. Please
          * note that this feature is buggy and some browsers do not implement it correctly
          */
         // 'exposed_headers' => array(),

         /**
          * Standard CORS requests do not send or set any cookies by default. For this to work,
          * the client must set the XMLHttpRequest's "withCredentials" property to "true". For
          * this to work, you must set this option to true so that the server can serve
          * the proper response header.
          */
         // 'allowed_credentials' => false,
    ),
);

in the config/autoload/zfr_cors.global.php

sorry, it is:

'allowed_origins' => array('*'),

bakura10 commented 10 years ago

Strange. It does not seem to be an issue of ZfrCors logic, but it just does not seem to initialize it. I'm not sure to understand why :/. Did you check that the configuration got properly loaded (for instance by dumping the 'Config' object from the service locator, and check if the ZfrCors config is present)?

ivanvujisic commented 10 years ago

I appreciate your effort to help, but my initial question was -> it "seems" to me I resolved CORS issue with Apigility using three simple PHP header commands (confirmed by AngularJS front end developer).

Would you be so kind to comment on this, is it security issue?

ivanvujisic commented 10 years ago

OK, I made progress, zfr-cors wasn't installed due to proc_open disabled in my php.ini So, uncaught php runtime exception is gone, say zfr-cors is installed properly.

Now I put:

'allowed_origins' => array('www.example.com'),

in the zfr_cors.global.php but api query still works. Any clue?

bakura10 commented 10 years ago

There is to be an exact match of origins. So for instance, in CORS, http://www.example.com is different than https://www.example.com, for instance. Try to add the exact origin that performs calls to your API.

bakura10 commented 10 years ago

If you're unsure, try to see a request made in your browser, and check thje "Origin" header in the request, and copy-paste this value into your config :).

ivanvujisic commented 10 years ago

Mate, I put

'allowed_origins' => array('http://www.ronaldmcdonald.com'),

but it still works properly, and I'm sure ZfrCors is loaded properly since there's no error trace in the error_log

bakura10 commented 10 years ago

Ok, just stupid thing (sorry, trying to find the obvious...): did you remove your hack from index.php as it had a wildcard ?

ivanvujisic commented 10 years ago

Yup, those two are separate ZF2 Apigility installations.

bakura10 commented 10 years ago

Hhmmm.

Can you please open the inspector (Safari, Chrome), and make a screenshot of the API request, along with the request and response headers?

ivanvujisic commented 10 years ago

API queries are triggered from AngularJS app, so I can't inspect from web browser, right?

bakura10 commented 10 years ago

Yes you can :). In Chrome all requests that are done asynchronously are logged in the inspector. In Safari, you have to open the section with a timer, and click on the grey circle on the left so that it listens to all AJAX requests.

cvuorinen commented 9 years ago

I was also having issues with zfrCors (more specifically this: https://github.com/zf-fr/zfr-cors/issues/14 and this: https://github.com/zf-fr/zfr-cors/issues/18) so I decided to set the CORS headers manually and disable zfrCors, since I don't need any advanced CORS functionality.

I ended up with a solution where I get the Response object in a module bootstrap and set the headers using that, which I think is a better way than calling the header function directly in index.php.

So I added the following method to a Module.php file:

    public function onBootstrap(MvcEvent $event)
    {
        // Set CORS headers to allow all requests
        $headers = $event->getResponse()->getHeaders();
        $headers->addHeaderLine('Access-Control-Allow-Origin: *');
        $headers->addHeaderLine('Access-Control-Allow-Methods: PUT, GET, POST, PATCH, DELETE, OPTIONS');
        $headers->addHeaderLine('Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept');
    }
bakura10 commented 9 years ago

Hi,

Actually, I've myself ended up NOT using ZfrCors. The problem is that you need to have the overhead of the whole application bootstrapping just to send headers.

A much better solution is actually to handle that on Apache side (although it offers you less flexibility, but for most cases, this is enough). Here is my VirtualHost for instance:

 <VirtualHost *:80>
          ServerName default
          ServerAlias *
          DocumentRoot "/var/www/html/public"

          # First, let's handle CORS requests
          SetEnvIfNoCase Access-Control-Request-Method "(GET|POST|PUT|DELETE|OPTIONS)" IsPreflight=1
          SetEnvIfNoCase Origin "https://(url1.com|url2.com)$" AccessControlAllowOrigin=$0

          Header always set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
          Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" env=IsPreflight
          Header always set Access-Control-Allow-Headers "Content-Type, Authorization, Accept" env=IsPreflight
          Header always set Access-Control-Max-Age "7200" env=IsPreflight

          RewriteEngine On
          RewriteCond %{REQUEST_METHOD} OPTIONS
          RewriteCond %{ENV:IsPreflight} 1
          RewriteRule ^(.*)$ $1 [R=200,L]

          <Directory "/var/www/html/public">
              DirectoryIndex index.php
              AllowOverride All
              Order allow,deny
              Allow from all

              RewriteEngine On
              RewriteCond %{REQUEST_FILENAME} -s [OR]
              RewriteCond %{REQUEST_FILENAME} -l [OR]
              RewriteCond %{REQUEST_FILENAME} -d
              RewriteRule ^.*$ - [NC,L]
              RewriteRule ^.*$ index.php [NC,L]
          </Directory>
      </VirtualHost>