According to the docs (5.1) it's possible to exclude URI's easily with the protected $except property, like so:
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'api/*'
];
Here I am trying to exclude the Set-Cookie :XSRF-TOKEN from API requests. However, this isn't working.
So I dug into Illuminate\Foundation\Http\Middleware\VerifyCsrfToken and became really confused. By the looks of it, the exclude will be ignored on any GET request, and therefore you get a cookie set. Here's the handle method:
public function handle($request, Closure $next)
{
if ($this->isReading($request) || $this->shouldPassThrough($request) || $this->tokensMatch($request)) {
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
}
isReading() will return true if the request is GET, rendering the results of shouldPassThrough() pointless. There is only 2 outcomes to this method too: 1) you get a cookie set, 2) you get an exception. I'd expect a third outcome of no cookie if the request matches an except item, right?
Also, looking at shouldPassThrough() seems backwards too. It returns true if the request matches an except item:
/**
* Determine if the request has a URI that should pass through CSRF verification.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function shouldPassThrough($request)
{
foreach ($this->except as $except) {
if ($request->is($except)) {
return true;
}
}
return false;
}
The method docs suggest that if it returns true it should pass through CSRF verification...i.e. get a cookie. But it only returns true if it matches an except item i.e. we don't want a cookie.
To get the behaviour I expect, I had to create my own addCookieToResponse method which only adds a cookie if the request doesn't match any except items;
protected function addCookieToResponse($request, $response)
{
if (!$this->shouldPassThrough($request)) {
$response = parent::addCookieToResponse($request, $response);
}
return $response;
}
Can somebody either validate this (as a bug?), or explain to me how it is intended to work please? Because what the docs (and code) imply don't make sense to me.