I am trying to make a POST-request to my Laravel API from Vue. The X-CSRF-TOKEN
header is set correctly (I see this in the POST-package sent to the server).
The route has the default web
-middleware.
Request
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Connection:keep-alive
Content-Length:2
Content-Type:application/json;charset=UTF-8
Host:api.xxx.local
Origin:http://manager.xxx.local
Referer:http://manager.xxx.local/location/planning/2
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
X-CSRF-TOKEN:EAf94SFJWBhlcwzxrq7nyygrnRSmZTavrnKYHv5C
X-Requested-With:XMLHttpRequest
Response
Request URL:http://api.xxx.local/locationplanning/deleteentry/15
Request Method:POST
Status Code:419 unknown status
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade
Error Stack:
{
"message": "",
"exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException",
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
"line": 203,
"trace": [
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
"line": 175,
"function": "prepareException",
"class": "Illuminate\\Foundation\\Exceptions\\Handler",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/app/Exceptions/Handler.php",
"line": 47,
"function": "render",
"class": "Illuminate\\Foundation\\Exceptions\\Handler",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 83,
"function": "render",
"class": "App\\Exceptions\\Handler",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 55,
"function": "handleException",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php",
"line": 49,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\View\\Middleware\\ShareErrorsFromSession",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
"line": 63,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Session\\Middleware\\StartSession",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php",
"line": 37,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php",
"line": 59,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Cookie\\Middleware\\EncryptCookies",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/app/Http/Middleware/ForgetDomainParameter.php",
"line": 30,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "App\\Http\\Middleware\\forgetDomainParameter",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 102,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 647,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 622,
"function": "runRouteWithinStack",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 588,
"function": "runRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 577,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 176,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 30,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 30,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 30,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php",
"line": 46,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 149,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
"line": 53,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 102,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 151,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 116,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/Applications/XAMPP/xamppfiles/htdocs/kitchenradar/server/public/index.php",
"line": 53,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
}
]
}
The problem here is that we're sending the request from one domain to another. The web frontend domain is manager.xxx.local while the API domain is api.xxx.local.
This is important because cookies are constrained to the domain they originate from. For security, the browser will not send cookies to a different domain by default. Laravel stores the CSRF token in the user's session for web requests and sends a session cookie to the browser to maintain this session.
Even though the bootstrap.js file, which ships with a default Laravel project, adds the CSRF token header for AJAX requests through axios, we still need a session so that Laravel can compare the token header to the value on the server.
We see the 419 HTTP status code—usually the result of a TokenMismatchException
—because the API request is not associated with a user session (no session cookie), so no CSRF token state exists in the session. Because of this, Laravel considers the token invalid for the API request.
We can solve this problem a couple of different ways. Modern browsers support Cross-Origin Resource Sharing (CORS) which, in part, allows us to share cookies between domains via AJAX. Our server needs to send the Access-Control-Allow-Credentials
header:
Access-Control-Allow-Credentials: true
Read this article on MDN for more information. The Laravel CORS package by Barry vd. Heuvel can help set this up if we don't want to manually configure our application or webserver.
Then, we can configure AJAX requests to forward cookies to a different domain by setting the following property on the XMLHttpRequest
:
xhr.withCredentials = true;
In axios, we can configure withCredentials
per request:
axios.get(url, { withCredentials: true });
...or set it as the default:
axios.defaults.withCredentials = true;
If we're using Vue Resource, we can set a similar configuration option:
Vue.http.options.credentials = true;
Even when we fix these issues, the current design relies on server-side sessions to maintain state for the CSRF token validation. Conventional APIs are typically stateless (no server-side session tied to a user) and authenticate requests using some form of access token (OAuth, JWT, etc.). Note that the CSRF token is not one such token.
For a more robust API framework, consider using Laravel Passport. We can add the CreateFreshApiToken
middleware that automatically generates an encrypted JWT to pass the user ID and CSRF token state so that we can build a sessionless API.
If we don't want to build a full API, such as if our AJAX requests just supplement the views generated by the server, we may not want to complicate the application by using a separate domain for these requests.
Notes:
It works if I deactivate it but I need the Auth.... Auth::user() is null when I deactivate it.
The user is null
not because we deactivated CSRF, but because we don't have a session to begin with (the session cookie wasn't sent to the API domain).
If I change the method to GET (in routes and axios) there is no error anymore
Laravel does not check the CSRF token for requests that semantically read data (GET, HEAD, and OPTIONS). It verifies the token only for requests that modify data (POST, PUT, DELETE, etc.).
Side note: this is important to remember because an attacker can forge a GET request that modifies a user's data if we don't set up our routes appropriately. Developers commonly make the mistake of using a link (often with .btn
) to make a simple request like deleting a record:
<a href="/posts/delete/{id}">Delete Post</a>
A malicious site can place the same link on a page and the request will bypass CSRF protection when clicked because the browser sends a GET request for the link.
It's hard to see what's going on without checking many places in the project, so I'll list some possible causes, and hope that it'll point you in the correct direction
First probable cause, the 419 http code is trigerred by some condition in your source code. This is higly unprobable given the callstack, but the error may be catched by some middleware and recreated (so wrong call stack).
Another probable cause, the CSRF protection. Given that it works for GET and not for POST (see comments), it's highly probable that the CSRF protection is triggered and refuse the request. To correct that, simply disable the CSRF middleware by using the api
(instead of web
) middleware OR adding the route into VerifyCsrfToken class. See How to disable some routes for CSRF validation
CSRF protection isn't needed for API routes, these are needed only for POST forms on "standard" webpages.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With