Forum

Another solution:

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier {

    protected $excludeRoutes = [
        'api*'
    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        foreach( $this->excludeRoutes as $route )
        {
            if( $request->is( $route ) ) return $next($request);
        }

        return parent::handle($request, $next);
    }

}

eduardostuart said:

Another solution:

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier {

  protected $excludeRoutes = [
      'api*'
  ];

  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  \Closure  $next
   * @return mixed
   */
  public function handle($request, Closure $next)
  {
      foreach( $this->excludeRoutes as $route )
      {
          if( $request->is( $route ) ) return $next($request);
      }

      return parent::handle($request, $next);
  }

}

this did the trick, Thank you.

Another possible solution (not ideal though):

<?php namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Route;
use Closure;

class VerifyCsrfToken extends BaseVerifier {

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $route = Route::getRoutes()->match($request);
        $routeAction = $route->getAction();
        if (isset($routeAction['nocsrf']) && $routeAction['nocsrf']) {
            return $next($request);
        }
        return parent::handle($request, $next);
    }

}

In your routes.php you may then disable the CSRF check for specific routes via

    Route::any('test', [
        'as' => 'external.test',
        'uses' => [email protected]',
        'nocsrf' => true,
    ]);

The solution is not ideal since we manually do the route matching which is done again after all global middlewares have finished (sendRequestThroughRouter). So we produce a little bit of an overhead there.

The advantage of this solution is that you can disable the CSRF check inside your routes.php and you don't need to change anyhting if your route name / URL ever changes. I.e. it is way easier to refactor / maintain your routes.

If you want to disable CSRF for every route, you can modify the Middleware/VerifyCsrfToken.php classes $except array to be like this:

protected $except = [
        '*',
];

This seems to be the cleanest way without modifying Laravel classes.

HolgerW1 said:

Another possible solution (not ideal though):

<?php namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Route;
use Closure;

class VerifyCsrfToken extends BaseVerifier {

   /**
    * Handle an incoming request.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Closure  $next
    * @return mixed
    */
   public function handle($request, Closure $next)
   {
       $route = Route::getRoutes()->match($request);
       $routeAction = $route->getAction();
       if (isset($routeAction['nocsrf']) && $routeAction['nocsrf']) {
           return $next($request);
       }
       return parent::handle($request, $next);
   }

}

In your routes.php you may then disable the CSRF check for specific routes via

   Route::any('test', [
       'as' => 'external.test',
       'uses' => [email protected]',
       'nocsrf' => true,
   ]);

The solution is not ideal since we manually do the route matching which is done again after all global middlewares have finished (sendRequestThroughRouter). Plus the match method does actually more than we really need (binding, ...).

The advantage of this solution is that you can disable the CSRF check inside your routes.php and you don't need to change anyhting if your route name / URL ever changes. I.e. it is way easier to refactor / maintain your routes.

You can in fact use Route::current() to get the currently matched route.

levacic said: You can in fact use Route::current() to get the currently matched route.

You unfortunately can't use Route::current() for global middlewares since the request to route matching is done after all global middlewares have finished. And the CSRF verifier is a global middleware for all routes by default.

This would work for route specific middlewares though since they run after the route matching.

HolgerW1 said:

You unfortunately can't use Route::current() for global middlewares since the request to route matching is done after all global middlewares have finished. And the CSRF verifier is a global middleware for all routes by default.

This would work for route specific middlewares though since they run after the route matching.

Oops! You are, of course, correct about that.

I'm new to L5 so I haven't yet had the time to dig into the framework and see how everything gets executed - but is there any specific reason why middleware runs before the route is matched? Is that so as to allow changing the request itself while it's on its way in? Even so, I'm not sure I would ever want to change the URL the user is trying to access, so this doesn't make much sense to me, though I'm likely overlooking something - it just seems like it would be possible to match the route before we run the middleware.

Thanks!

levacic said:

Oops! You are, of course, correct about that.

I'm new to L5 so I haven't yet had the time to dig into the framework and see how everything gets executed - but is there any specific reason why middleware runs before the route is matched? Is that so as to allow changing the request itself while it's on its way in? Even so, I'm not sure I would ever want to change the URL the user is trying to access, so this doesn't make much sense to me, though I'm likely overlooking something - it just seems like it would be possible to match the route before we run the middleware.

Thanks!

I honestly don't know the actual reason behind this design, you should ask Graham Campbell or Taylor Otwell.

Here's the code in Kernel.php responsible for this behavior:

    return (new Pipeline($this->app))
                    ->send($request)
                    ->through($shouldSkipMiddleware ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());

As you can see dispatchToRouter is called after the global middlewares have finished (through command).

Imho it would be helpful if the request to route matching would be done before the global middlewares are being executed and thus if the middlewares would have access to the route data. But I doubt Graham or Taylor will change this since it would mean some fundamental restructuring of that specific code.

Btw. here's the 'official' answer to that from Taylor Otwell: https://github.com/laravel/framework/pull/9708

Riffing off what folks have done so far, I did the following.

In a new config file (so I don't run into upgrade collisions) located at config/custom.php I created this:

return [

    /*
    |--------------------------------------------------------------------------
    | CSRF Exemptions
    |--------------------------------------------------------------------------
    |
    | Routes we do not check for CSRF. Note: Should use other means of 
    | authenticating the origin, etc.
    |
    */

    'csrf_exempt_routes' => array(
        'api/*',
    ),

];

Note that you can use specific routes like foo/bar or the wildcard foo/* to match. Not sure if it allows more complex regex. Also, do not include a leading /.

Then, in app/Http/Middleware/VerifyCsrfToken.php I set the $except variable to the values specified in the config like so:

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Illuminate\Contracts\Encryption\Encrypter;
use Config;


class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [];


    /**
     * Create a new middleware instance. We set our exceptions in a 
     * config file.
     *
     * @param  \Illuminate\Contracts\Encryption\Encrypter  $encrypter
     * @return void
     */
    public function __construct(Encrypter $encrypter)
    {
        parent::__construct($encrypter);
        $this->except = Config::get('custom.csrf_exempt_routes');
    }       
}

Hope this method helps someone.

Extra Credit

I plan to subclass Route eventually so I can pass a parameter into the route definition like:

public function __construct($methods, $uri, $action, $options)

...

$options = array('disable_csrf' => true);
Route::get('/foo/', function () { ... }, $options);

...

That way, there is no need to duplicate route specs in the config, which is a tad error prone.

Not sure if this is new to 5.1: http://laravel.com/docs/5.1/routing#csrf-excluding-uris

You can exclude URIs from CSRF by simply adding them to the $except property of the VerifyCsrfToken middleware:

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'api/*',
    ];
}

Documentation: http://laravel.com/docs/5.1/routing#csrf-protection

hi AlexanderWright, you save my time

In laravel 5.3 To exclude a path from csrf middleware check open app\Http\Middleware\VerifyCsrfToken.php file and add the path to the $except array. like this

protected $except = [ // "api/" //every route starting with /api/ should be ignored for csrf check ];

note the use of wildcard in the route definition

What about using the $except array and wildcards?

 protected $except = [
    'mandrill/*',
    'pusher/*',
];

That's worked without having to redefine the VerifyCSRFToken middleware for me.

Laravel 4.2

filters.php

Route::filter('csrf', function () {
    if(Config::get('auth.no_csrf')){
        return;
    }

    if (Session::token() !== Input::get('_token')) {
        throw new Illuminate\Session\TokenMismatchException('CSRF token not provided', 401);
    }
});

routes.php

Route::group(['after' => 'no-cache', 'prefix' => 'api/v1'], function () {
    // Disable CSRF protection
    Config::set('auth.no_csrf', true);

    Route::post('/test/search', ['auth.no_csrf' => 'val','uses' => 'Controllers\Api\[email protected]']);
});