├── Api
└── CorsCheckInterface.php
├── LICENSE
├── Model
└── CorsCheck.php
├── Plugin
├── CorsHeadersPlugin.php
├── CorsRequestMatchPlugin.php
└── CorsRequestOptionsPlugin.php
├── README.md
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ └── system.xml
├── config.xml
├── di.xml
├── module.xml
└── webapi.xml
└── registration.php
/Api/CorsCheckInterface.php:
--------------------------------------------------------------------------------
1 | " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
49 |
--------------------------------------------------------------------------------
/Model/CorsCheck.php:
--------------------------------------------------------------------------------
1 | response = $response;
28 | $this->request = $request;
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function check()
35 | {
36 | // respond to OPTIONS request with appropriate headers
37 | $this->response->setHeader('Access-Control-Allow-Methods', $this->request->getHeader('Access-Control-Request-Method'), true);
38 | $this->response->setHeader('Access-Control-Allow-Headers', $this->request->getHeader('Access-Control-Request-Headers'), true);
39 | return '';
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Plugin/CorsHeadersPlugin.php:
--------------------------------------------------------------------------------
1 | response = $response;
41 | $this->scopeConfig = $scopeConfig;
42 | }
43 |
44 | /**
45 | * Get the origin domain the requests are going to come from
46 | * @return string
47 | */
48 | protected function getOriginUrl()
49 | {
50 | return $this->scopeConfig->getValue('web/corsRequests/origin_url',
51 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
52 | }
53 |
54 | /**
55 | * Get the origin domain the requests are going to come from
56 | * @return string
57 | */
58 | protected function getAllowCredentials()
59 | {
60 | return (bool) $this->scopeConfig->getValue('web/corsRequests/allow_credentials',
61 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
62 | }
63 |
64 | /**
65 | * Get the origin domain the requests are going to come from
66 | * @return string
67 | */
68 | protected function getEnableAmp()
69 | {
70 | return (bool) $this->scopeConfig->getValue('web/corsRequests/enable_amp',
71 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
72 | }
73 |
74 | /**
75 | * Get the Access-Control-Max-Age
76 | * @return string
77 | */
78 | protected function getMaxAge()
79 | {
80 | return (int) $this->scopeConfig->getValue('web/corsRequests/max_age',
81 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
82 | }
83 |
84 | /**
85 | * Triggers before original dispatch
86 | * This method triggers before original \Magento\Webapi\Controller\Rest::dispatch and set version
87 | * from request params to VersionManager instance
88 | * @param FrontControllerInterface $subject
89 | * @param RequestInterface $request
90 | * @return void
91 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
92 | */
93 | public function beforeDispatch(
94 | FrontControllerInterface $subject,
95 | RequestInterface $request
96 | ) {
97 | if ($originUrl = $this->getOriginUrl()) {
98 | $this->response->setHeader('Access-Control-Allow-Origin', rtrim($originUrl,"/"), true);
99 | if ($this->getAllowCredentials()) {
100 | $this->response->setHeader('Access-Control-Allow-Credentials', 'true', true);
101 | }
102 | if ($this->getEnableAmp()) {
103 | $this->response->setHeader('AMP-Access-Control-Allow-Source-Origin', rtrim($originUrl,"/"), true);
104 | }
105 | if ((int)$this->getMaxAge() > 0) {
106 | $this->response->setHeader('Access-Control-Max-Age', $this->getMaxAge(), true);
107 | }
108 | }
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/Plugin/CorsRequestMatchPlugin.php:
--------------------------------------------------------------------------------
1 | request = $request;
41 | $this->routeFactory = $routeFactory;
42 | }
43 |
44 | /**
45 | * Generate the list of available REST routes. Current HTTP method is taken into account.
46 | *
47 | * @param \Magento\Webapi\Model\Rest\Config $subject
48 | * @param $proceed
49 | * @param Request $request
50 | * @return \Magento\Webapi\Controller\Rest\Router\Route
51 | * @throws \Magento\Framework\Webapi\Exception
52 | */
53 | public function aroundMatch(
54 | Router $subject,
55 | callable $proceed,
56 | Request $request
57 | )
58 | {
59 | try {
60 | $returnValue = $proceed($request);
61 | } catch (\Magento\Framework\Webapi\Exception $e) {
62 | $requestHttpMethod = $this->request->getHttpMethod();
63 | if ($requestHttpMethod == 'OPTIONS') {
64 | return $this->createRoute();
65 | } else {
66 | throw $e;
67 | }
68 | }
69 | return $returnValue;
70 | }
71 |
72 | /**
73 | * Create route object to the placeholder CORS route.
74 | *
75 | * @return \Magento\Webapi\Controller\Rest\Router\Route
76 | */
77 | protected function createRoute()
78 | {
79 | /** @var $route \Magento\Webapi\Controller\Rest\Router\Route */
80 | $route = $this->routeFactory->createRoute(
81 | 'Magento\Webapi\Controller\Rest\Router\Route',
82 | '/V1/cors/check'
83 | );
84 |
85 | $route->setServiceClass('SplashLab\CorsRequests\Api\CorsCheckInterface')
86 | ->setServiceMethod('check')
87 | ->setSecure(false)
88 | ->setAclResources(['anonymous'])
89 | ->setParameters([]);
90 |
91 | return $route;
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/Plugin/CorsRequestOptionsPlugin.php:
--------------------------------------------------------------------------------
1 | isGet() && !$subject->isPost() && !$subject->isPut() && !$subject->isDelete() && !$subject->isOptions()) {
32 | throw new InputException(__('Request method is invalid.'));
33 | }
34 | return $subject->getMethod();
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Magento 2 CORS Cross-Domain Requests by SplashLab
2 |
3 | This module allows you to enable Cross-Origin Resource Sharing (CORS) REST API requests in Magento 2 by adding the appropriate HTTP headers and handling the pre-flight OPTIONS requests.
4 |
5 | This can be used to allow AJAX and other requests to the Magento 2 REST API from another domain (or subdomain).
6 |
7 | ## How to install
8 |
9 | ### 1. via composer
10 |
11 | Edit `composer.json`
12 |
13 | ```
14 | {
15 | "repositories": [
16 | {
17 | "type": "vcs",
18 | "url": "https://github.com/splashlab/magento-2-cors-requests"
19 | }
20 | ],
21 | "require": {
22 | "splashlab/magento-2-cors-requests": "dev-master"
23 | }
24 | }
25 | ```
26 |
27 | ```
28 | composer install
29 | php bin/magento setup:upgrade
30 | php bin/magento setup:static-content:deploy
31 | ```
32 |
33 | ### 2. Copy and paste
34 |
35 | Download latest version from GitHub
36 |
37 | Paste into `app/code/SplashLab/CorsRequests` directory
38 |
39 | ```
40 | php bin/magento setup:upgrade
41 | php bin/magento setup:static-content:deploy
42 | ```
43 |
44 | ### 3. Update Origin URL
45 |
46 | In `Stores -> Configuration`, go to `General -> Web -> CORS Requests Configuration`.
47 |
48 | Then edit the the `CORS Origin Url` field to the domain you want to enable cross-domain requests from. (i.e. http://example.com)
49 |
50 | ## How does it work?
51 |
52 | The full implementation of CORS cross-domain HTTP requests is outside the scope of this README, but this is what this module does:
53 |
54 | 1. Allows onfigureing an Origin Url in the Admin Configuration area - this is the domain which cross-domain requests are permitted from
55 | 2. This domain is added to a `Access-Control-Allow-Origin` response HTTP header
56 | 3. Optionally you can enable the `Access-Control-Allow-Credentials` header as well, to enable passing cookies
57 |
58 | For non-GET and non-standard-POST requests (i.e. PUT and DELETE), the "pre-flight check" OPTIONS request is handled by:
59 |
60 | 1. An empty `/V1/cors/check` API response with the appropriate headers:
61 | 2. `Access-Control-Allow-Methods` response header, which mirrors the `Access-Control-Request-Method` request header
62 | 3. `Access-Control-Allow-Headers` response header, which mirrors the `Access-Control-Request-Headers` request header
63 |
64 | ### Alternative Solutions
65 |
66 | You can also manage these CORS headers with Apache and Nginx rules, instead of using this extension:
67 |
68 | - https://community.magento.com/t5/Magento-2-Feature-Requests-and/API-CORS-requests-will-fail-without-OPTIONS-reponse/idi-p/60551
69 | - https://stackoverflow.com/questions/35174585/how-to-add-cors-cross-origin-policy-to-all-domains-in-nginx
70 |
71 | But I created this extension to allow you to configure the Origin domain the Admin Configuration, and to avoid having to create and manage special server configuration.
72 |
73 | ## CORS Cross-Domain Request References
74 |
75 | - https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
76 | - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
77 | - https://www.html5rocks.com/en/tutorials/cors/
78 | - https://stackoverflow.com/questions/29954037/how-to-disable-options-request
79 | - https://stackoverflow.com/questions/12320467/jquery-cors-content-type-options
80 | - https://github.com/magento/magento2/issues/8399
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lpouwelse/magento-2-cors-requests",
3 | "description": "Enabling cross-origin resource sharing (CORS) requests to Magento 2 API from configured Origin domain",
4 | "homepage": "https://github.com/splashlab/magento-2-cors-requests",
5 | "type": "magento2-module",
6 | "version": "100.0.7",
7 | "license": [
8 | "OSL-3.0",
9 | "AFL-3.0"
10 | ],
11 | "support": {
12 | "issues": "https://github.com/splashlab/magento-2-cors-requests/issues",
13 | "source": "https://github.com/splashlab/magento-2-cors-requests"
14 | },
15 | "require": {
16 | "php": "~7.1.3||~7.2.0||~7.3.0||~7.4.0||~8.1.0",
17 | "magento/framework": "*"
18 | },
19 | "autoload": {
20 | "files": [
21 | "registration.php"
22 | ],
23 | "psr-4": {
24 | "SplashLab\\CorsRequests\\": ""
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/etc/adminhtml/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | SplashLab_CorsRequests::config
7 |
9 |
10 |
12 |
13 | *, or fully qualified URLs without trailing '/' (slash) (e.g. http://example.com)
14 |
15 |
16 |
18 |
19 | Magento\Config\Model\Config\Source\Yesno
20 | Enables Access-Control-Allow-Credentials response header to pass cookies
21 |
22 |
24 |
25 | Magento\Config\Model\Config\Source\Yesno
26 | Enables AMP-Access-Control-Allow-Source-Origin response header for AMP CORS requests
27 |
28 |
30 |
31 | Enables Access-Control-Max-Age response header for CORS requests (max age in seconds)
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | *
8 | 0
9 | 0
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/etc/webapi.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |