Skip to content

Instantly share code, notes, and snippets.

@rordi
Last active March 21, 2022 14:04
Show Gist options
  • Save rordi/42de5982e877e1177a0e6a98ee8415ea to your computer and use it in GitHub Desktop.
Save rordi/42de5982e877e1177a0e6a98ee8415ea to your computer and use it in GitHub Desktop.
Simplest, configurable CORS implementation for Symfony through listeners

Simplest, configurable CORS implementation for Symfony through listeners

CorsListener.php event listener for pre-flight OPTIONS requests

<?php
/**
 * CorsListener.php
 *
 * @author Dietrich Rordorf
 * @since 05/2017
 *
 */

namespace AppBundle\EventListener;

use AppBundle\Model\Security\CORS;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class CorsListener implements EventSubscriberInterface
{

    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::REQUEST  => array('onKernelRequest', 9999),
            KernelEvents::RESPONSE => array('onKernelResponse', 9999),
        );
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }
        $request = $event->getRequest();
        $method  = $request->getRealMethod();
        if ('OPTIONS' === strtoupper($method)) {
            $response = new Response();
            $event->setResponse($response);
        }
    }

    public function onKernelResponse(FilterResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        // get website url, from which the user is accessing, otherwise the default host (Access-Control-Allow-Origin header only support one value)
        $host = CORS::CORS_DEFAULT_HOST;

        $referer = $event->getRequest()->server->get('HTTP_REFERER');
        if ($referer) {
            $parts = parse_url($referer);
            if (is_array($parts) && array_key_exists('scheme', $parts) && array_key_exists('host', $parts)) {
                $referer = $parts["scheme"] . "://" . $parts["host"];
                if(isset($parts['port'])) {
                    $referer .= ':' . $parts['port'];
                }
                if(in_array($referer, CORS::CORS_ALLOWED_HOSTS)) {
                    $host = $referer;
                }
            }
        } elseif ($referer === null) {
            $host = '*';
        }

        $response = $event->getResponse();
        $response->headers->set('Access-Control-Allow-Origin', $host);
        $response->headers->set('Access-Control-Allow-Headers', CORS::CORS_ALLOWED_HEADERS);
        $response->headers->set('Access-Control-Expose-Headers', CORS::CORS_ALLOWED_HEADERS);
        $response->headers->set('Access-Control-Allow-Methods', CORS::CORS_ALLOWED_METHODS);
        $response->headers->set('Access-Control-Max-Age', CORS::CORS_MAX_AGE);
    }
}

CORS.php configuration

<?php
/**
 * CORS.php
 *
 * @author Dietrich Rordorf
 * @since 05/2017
 *
 */

namespace AppBundle\Model\Security;

class CORS
{

    // cross-site requests allowed domains
    const CORS_ALLOWED_HOSTS = [
        'https://prod-env',
        'https://qa-env',
        'https://dev-env',
        'http://local-env',
        'http://localhost',
        'http://localhost:3001',  // CORS is port and scheme "sensitive" !
        'http://localhost:8080'
        // ...
    ];

    // the default CORS allowed origin response
    const CORS_DEFAULT_HOST = 'https://prod-env';

    // non-trivial headers allowed in API requests, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
    const CORS_ALLOWED_HEADERS = 'Authorization, Content-Length, Content-Type, X-Requested-With';

    // max time in seconds a browser can cache the CORS settings
    const CORS_MAX_AGE = 120;

    // allowed HTTP methods
    const CORS_ALLOWED_METHODS = 'GET, POST, PUT, HEAD';

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment