0

In my Laravel 5.4, I use the following code in my ajax using jQuery:

            $.ajax({
                url     : 'http://example.com/addmember',
                method    : 'POST',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                },
                beforeSend  : function()
                {},
                data : $('#theForm').serialize(),
                success   : function(response)
                {
                   // I do something here
                },
                error : function(e)
                {
                    console.log(e);
                },
                complete : function(c){
                }
            });

I sometimes get a token mismatch exception like so: enter image description here I generally get this error when the user stays on the page for a very long time before firing the AJAX request.

How can i handle the situation ?

I even made a middleware that upon a token mismatch on an ajax call returns a response like response.status == "TOKEN-ERROR" on getting this i reload the page using window.loaction.reload(1);.

Is there any more efficient method to handle the situation without reloading the page and thus loosing the user's progress ?

Shakti Phartiyal
  • 6,156
  • 3
  • 25
  • 46
  • Check this : https://stackoverflow.com/questions/53684928/how-to-automatically-add-x-csrf-token-with-jquery-ajax-request-in-laravel – Prateek Dec 08 '18 at 19:33

2 Answers2

2

In your app/Exceptions/Handler.php file

Add a handler for TokenMismatchException in the render method

public function render($request, Exception $exception)
{
    if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
        if ($request->expectsJson()) {
            return response()->json([
                'error' => 'Token mismatch'
            ], $exception->getStatusCode());
        };
    }

    return parent::render($request, $exception);
}

This will return an error json response. You can customize the error response to suit your needs.

Mayank Pandeyz
  • 25,704
  • 4
  • 40
  • 59
Sandeesh
  • 11,486
  • 3
  • 31
  • 42
  • 1
    Fair enough.. I already get the json i want as i mentioned in the question. how do I handle this error without refreshing the page? i.e get the correct (non-expired) CSRF token ? – Shakti Phartiyal May 17 '17 at 19:44
  • One way would to be to use a token generating route to fetch a fresh token on token mismatch error. – Sandeesh May 17 '17 at 19:49
  • How can i do that ? – Shakti Phartiyal May 17 '17 at 20:16
  • Route::get('csrf-token', function () { return response()->json([ 'token' => csrf_token() ]); }); – Sandeesh May 18 '17 at 04:46
  • And do i need to set this token into the cookie / meta tag or should i use it as is ? – Shakti Phartiyal May 18 '17 at 04:51
  • Since you're setting the '_token' field in your form data on page load, you might need to change this with the newly retrieved token. But ideally you could store the token as a meta info and then fetch and use it in ajax request headers. This way when you fetch a new token via the new route, you can set the meta tag with the new token and all your ajax requests will use the new token without needing to change them. More info https://laravel.com/docs/5.4/csrf#csrf-x-csrf-token – Sandeesh May 18 '17 at 04:57
  • Is this not a security concern? as CSRF token prevents hits from third party sites or apps like postman / scripts. So if I am returning a new token will this not cause the ineffectiveness of the CSRF security ? – Shakti Phartiyal May 18 '17 at 04:59
  • Definitely is, and i assume this is an authenticated route where the form lives. So i would suggest having the csrf route wrapped with the auth middleware to prevent misuse. This isn't the ideal usage, but if you need good user experience without having to refresh the page for a user then you might go this route. Another option i would suggest is to handle the tokenmismatch exception and instead of throwing an error response, refresh the page with the form data to auto populate the forms along with an error message asking them to resubmit. – Sandeesh May 18 '17 at 05:10
  • Thank you seems like a legit point. Wrapping up the route in Auth middleware will solve the issue.. – Shakti Phartiyal May 18 '17 at 05:13
  • Hey, I just came up with this issue. I'm running on Laravel 5.6 and when I try to get status code using `$exception->getStatusCode()`, I get error saying `Call to undefined method Illuminate\Session\TokenMismatchException::getStatusCode()`. Need some quick tips here. – Birendra Gurung Jun 12 '18 at 06:06
0

When I'm using an Ajax call, I add the _token attribute to the data:

$("input").on("someFunction", function(event) {
      var score = $(this).val();
      var scoreId = $(this).data('score-id');
      $("#" + scoreId).text(event.value);
      console.log(scoreId + ' => ' + score);
      var data = {
         "_token": "{{ csrf_token() }}",
         "score": score,
         "scoreId" : scoreId
      };
      $.ajax({
          data: data,
          type: "POST",
          url: '{!! route('score.store', $id) !!}',
          success: function (result) {
          console.log(result);
      },
      error: function (xhr, status, error) {
         var err = eval("(" + xhr.responseText + ")");
         alert(err.error);
      }
   });
});

In my web.php I add this item to the route:

 Route::post('/path/to/route/{id}/score/store', [
    'before' => 'csrf',
    'as' => 'score.store',
    'uses' => 'Score\ScoreController@saveScore'
 ]);
Arthur
  • 921
  • 3
  • 13
  • 25
  • I suppose `"_token": "{{ csrf_token() }}",` and `headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },` does the same thing. also please explain `middleware('can:update-score,App\Score');` – Shakti Phartiyal May 17 '17 at 19:42