rickyduck
rickyduck

Reputation: 4084

Magento 2 Token Auth - 400 Error / CORS issue

I'm using token authentication within my app to access the magento API. I do the following setup in postman and it works fine: enter image description here

However using jQuery post I consistently get a 400 error back as it doesn't seem to support HTTP verb OPTIONS for preflight (I have a CORS config for nginx). I've tried:

$.ajax({
  method: "POST",
  url: `myip/index.php/rest/V1/integration/admin/token`,
  data: {username: 'ember-app', password: 'ember-app2'},
}).done(function(response) {
  alert(response);
});

$.ajax({
  method: "POST",
  url: `myip/index.php/rest/V1/integration/admin/token?username=ember-app&password=ember-app2`
}).done(function(response) {
  alert(response);
});

$.post(
  'myip/index.php/rest/V1/integration/admin/token', 
  {username: 'ember-app', password: 'ember-app2'},
  function(response) { alert(response); }
);

I've also tried JSON.stringify around the object. Why does it work in postman but in xhr I constantly get a 400 error? Furthermore this request then triggers another request for a GET with returns a cors error. Some my $.post causes this in the console:

enter image description here

Here's some cURL responses:

curl -H "Origin: http://localhost:4200" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: X-Requested-With" \
  -d '{"username": "ember-app", "password": "ember-app2"}'\
  -X OPTIONS --verbose https://myhost/index.php/rest/V1/integration/admin/token

gives a response that is:

{"message":"Request method is invalid."} 

Same with:

curl -H "Origin: http://localhost:4200" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: X-Requested-With" \
  -X OPTIONS --verbose https://myhost/index.php/rest/V1/integration/admin/token?username=ember-app&password=ember-app2

However doing a normal curl post works fine:

curl -H "Content-Type: application/json" -X POST -d '{"username":"ember-app","password":"ember-app2"}' https://myhos/index.php/rest/V1/integration/admin/token

Upvotes: 1

Views: 3265

Answers (3)

Tejeswar Pratap
Tejeswar Pratap

Reputation: 11

Include below lines in Magento .htaccess file, present in the root folder of Magento. It worked for me.

Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"

# Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request.
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

Upvotes: 0

HiDeoo
HiDeoo

Reputation: 10563

A request is identified as a cross-origin HTTP request when it requests a resource from a different domain than the one which serves the first resource. For security reasons, cross-origin HTTP requests initiated from within scripts are restricted in browsers.

The Cross-Origin Resource Sharing (CORS) mechanism from the W3C defines a standard which give web servers cross-domain access controls that enable secure cross-domain data transfers. It works by adding new HTTP headers that allow servers to defines the set of origins that are permitted to access that information using a web browser.

Additionally, some HTTP requests are considered by default non-secure when they can modify user data. These requests are automatically preflight in web browsers. That means before a request is sent, a preflight request with the OPTIONS verb will be sent by the browser to the other domain server in order to determine whether the actual request is safe to send or not. Upon approval from the server, the actual request is sent.

When you're using Postman, your actual request is sent and that's it. No CORS protection or anything, Postman is not a web browser. It just works.

When you're using jQuery to make an AJAX call in a script from you web browser, it'll follow the CORS standard and tag your request are unsafe, which means it should be preflight. It'll then send a first request to your server with the OPTIONS method to check if your actual request is safe. These requests needs to be allowed on your web server (which seems to be Nginx).

You can check this example on how to enable CORS on Nginx and maybe compare it with your actual configuration. A basic wide-open configuration would be (I added some comments):

location / {
  // OPTIONS requests.
  if ($request_method = 'OPTIONS') {
    // URI that may access the resource.
    add_header 'Access-Control-Allow-Origin' '*';

    // Methods allowed when accessing the resource.
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

    // Headers that can be used when making the actual request.
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

    // Cache the preflight result for 20 day.
    add_header 'Access-Control-Max-Age' 1728000;

    add_header 'Content-Type' 'text/plain charset=UTF-8';
    add_header 'Content-Length' 0;

    return 204;
  }

  // POST requests.
  if ($request_method = 'POST') {
    // URI that may access the resource.
    add_header 'Access-Control-Allow-Origin' '*';

    // Methods allowed when accessing the resource.
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

    // Headers that can be used when making the actual request.
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
  }

  // GET requests
  if ($request_method = 'GET') {
    // URI that may access the resource.
    add_header 'Access-Control-Allow-Origin' '*';

    // Methods allowed when accessing the resource.
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

    // Methods allowed when accessing the resource.
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
  }
}

If everything on the server-side is in order, you should adjust your jQuery request to match what's expected on the server.

jQuery.ajax({
  url: 'https://host/rest/V1/integration/admin/token',
  data: JSON.stringify({"username": "ember-app", "password": "ember-app2"}),
  contentType: "application/json",
  method: 'POST'
}).done((response) => {
  alert(response);
})

Upvotes: 5

Daniel E
Daniel E

Reputation: 9

It looks like the CORS error is a red herring in this instance.

You mention Magento endpoint /V1/integration/admin/token which is used for token generation.

You do not need to use this within your app.

Instead this can be a multi step process.

  1. Generate the token http://devdocs.magento.com/guides/v2.0/get-started/authentication/gs-authentication-token.html#auth-request

  2. Store the token in a config file

  3. For any web API request specify the token in the Authorization request header with the Bearer HTTP authorization scheme. http://devdocs.magento.com/guides/v2.0/get-started/authentication/gs-authentication-token.html#web-api-access

Upvotes: 0

Related Questions