5

I'm building a CMS-like web application using Laravel(back-end) and ReactJS with JQuery(front-end).

I decide to put the existing Web API into a separate domain(api.test.com), and my user interface is on the different domain(test.com).

On test.com, I launch an ajax request to api.test.com to modify some resource on the server:

  $.ajax({
    url: "api.test.com",
    method: 'POST',
    data: {...}
    success: function (no) {
    // ...
    }
  });

And of course it's illegal due to security problem. However, I can configure my web server:

For Nginx:

  add_header Access-Control-Allow-Origin http://test.com;
  add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
  add_header Access-Control-Allow-Headers X-Requested-With,X-CSRF-TOKEN,X-XSRF-TOKEN;

The Access-Control-Allow-Origin problem is solved perfectly but another problem occurs due to Laravel's CSRF protection...

Laravel requires a CSRF token included in the request(POST,PUT...which will modify the resource) by default.

In my case, I must generate csrf_token on api.test.com rather than test.com because different domain do not share the token.

I followed the User Guide of Laravel and added these code to my front-end:

  $.ajax({
    url: "api.test.com/token", // simply return csrf_token();
    method: "GET",
    success: function (token) {
      // Now I get the token
      _token = token;
    }.bind(this)
  });

and modify the previous request implementation:

  $.ajax({
    url: "api.test.com",
    method: 'POST',
    headers: {
      "X-CSRF-TOKEN": _token // Here I passed the token
    },
    data: {...}
    success: function (no) {
    // ...
    }
  });

But Laravel responses a status code of 500. Then I checked the VerifyCsrfToken.php:

protected function tokensMatch($request)
{
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
    if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
        $token = $this->encrypter->decrypt($header);
    }
    // Log::info($request->session()->token() . " == $token");
    return Str::equals($request->session()->token(), $token);
}

The $token which I 'POST' to is different from what it was ($request->session()->token()).

I found that the validation tokens on server are different when calling $.ajax.

I try to put the two requests in the same session(by changing the cookie), but it's impossible.

I spent a lot of time to solve this problem but didn't work it out.

Have any idea or solution?

Thanks, Micooz

1
  • I'd add code to VerifyCsrfToken that exempts your API routes from the need for a CSRF token. Commented Jul 16, 2015 at 14:59

3 Answers 3

4

Thank you for answering my question. I've considered disabling the CSRF protection to some URIs but I don't want to take these risk.

The key point of my question is that the $.ajax forgets carrying cookies before request, and resulting token validation failed.

Now I setup the JQuery Ajax, let it carry cookies before make a request.

  $.ajaxSetup({
    xhrFields: { withCredentials: true }
  });

and Nginx conf:

  add_header Access-Control-Allow-Credentials true;

BTW, it's not necessary to include the token in the data:{}(Form).

All the problems are settled and it works perfectly for me.

Sign up to request clarification or add additional context in comments.

Comments

0

Laravel expects the token as a data variable, included on your fields, the name of the var needs to be _token try to change it.

Another solution is including the token in your data not in the headers.

  $.ajax({
    url: "api.test.com",
    method: 'POST',
    data: { _token : _token }
    success: function (no) {
    // ...
    }
  });

1 Comment

Yes, I've tried this solution but it didn't work. I guess I got a wrong token from api.test.com because of the two different sessions made by $.ajax.
-1

You can follow this url

http://laravel.io/forum/11-14-2014-disabling-the-csrf-middleware-in-laravel-5

In this link, you need to wrap up VerifyCsrfToken class with new one class where you specify actions on which you want not use csrf_token

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.