Below are the steps to configure CORS (Cross Origin Resource Sharing) in the server(Magento) to call its API in frontend (Angular 2).
The below procedure is a fix for different CORS based error scenarios like:
- Response for preflight has invalid HTTP status code 400
- Response for preflight has invalid HTTP status code 405
- No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://10.22.1.168:3000' is therefore not allowed access.
- Request header field authorization is not allowed by Access-Control-Allow-Headers
- Request header field content-type is not allowed by Access-Control-Allow-Headers
Step 1: In the .htacess of Magento,under <IfModule mod_rewrite.c> replace
RewriteCond %{REQUEST_METHOD} ^TRAC[EK]
with
RewriteCond %{REQUEST_METHOD} OPTIONS
Step 2: RewriteRule .* - [L,R=405]
with
RewriteRule ^(.*)$ $1 [R=200,L]
Step 3: To the end of the page, there is another <IfModule mod_headers.c> (around line ~120)
Under this tag,add the below 2 lines:
Header
always set Access-Control-Allow-Origin "*"
Header
always set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
Note: Dont forget the word 'always'.
Step 4:
Make sure to activate the apache module headers:
a2enmod headers
After changes my .htaccess looks like:
... ....
....
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine on
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#RewriteCond %{REQUEST_METHOD} ^TRAC[EK]
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
#RewriteRule .* - [L,R=405]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule .* index.php [L]
</IfModule>
...
...
ErrorDocument 404 /pub/errors/404.php
ErrorDocument 403 /pub/errors/404.php
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
Header always set X-UA-Compatible "IE=edge"
<FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|webmanifest|woff2?|xloc|xml|xpi)$">
Header unset X-UA-Compatible
</FilesMatch>
</IfModule>
My Frontend (Angular 2) logic looks something like:
import { Injectable } from '@angular/core';
import { Http, Response,Headers,RequestOptions,RequestMethod} from '@angular/http';
export class ProductService {
public _productUrl = 'http://10.22.1.168/Mage_ang2/index.php/rest/V1/customers/1';
constructor(private _http: Http) { }
getProducts(): Observable<IProduct[]>
{
let headers = new Headers({'Content-Type': 'application/json;charset=UTF-8',
'Authorization':'Bearer ntthnrbj1uam2tuv1ekva7n18mcnkby3'
});
return this._http.get(this._productUrl,{headers:headers})
.map(response => {
return response.json();
});
}
The Authorization Bearer key was generated as below:
To get the bearer token:
1) Login to Magento admin, create a user role and a user in admin
2) To get bearer token :
curl -X POST "http://<localhost/IP>/Magento2/index.php/rest/V1/integration/customer/token" -H "Content-Type: application/json" -d '{"username":"restuser_name","password": "restuser_password"}'
Explanation :
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
Apache will respond "200 OK" when the request method is OPTIONS
Header set Access-Control-Allow-Origin "*"
Giving global cross-origin access to Magento. Usually its a bad idea to give * from a security perspective.
Suggested way is to give the the origin(domain+scheme+port number) site URL in place of *.
Eg: Access-Control-Allow-Origin: http://siteA.com
ie) When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin
response header to tell the browser that the content of this page is accessible to certain origins.
By default, Site B's pages are not accessible to any other origin; using the Access-Control-Allow-Origin
header opens a door for cross-origin access by specific requesting origins.
Header always set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization
Each header must explicitly be listed and I added x-test-header
to Access-Control-Allow-Headers
References:
*
https://benjaminhorn.io/code/setting-cors-cross-origin-resource-sharing-on-apache-with-correct-response-headers-allowing-everything-through/
*
http://stackoverflow.com/questions/32500073/request-header-field-access-control-allow-headers-is-not-allowed-by-itself-in-pr