I am trying to save a customer shopping cart in a session on the server side. The customer does not need to log in. So no Laravel Sanctum or Passport etc. Laravel and Vue are running on different servers (Backend: localhost:8000 and Frontend: localhost:9000).
Here are my api.php routes.
Route::get('get-token', function (Request $request) {
return [
'_token' => $request->session()->token()
];
})->middleware('web');
Route::group(['prefix'=>'v1'], function() {
Route::resource('cart', App\Http\Controllers\Api\V1\CartController::class)->middleware('web');
Route::resource('products', App\Http\Controllers\Api\V1\ProductController::class);
}
The GET /cart requests all work fine via axios and insomnia:
here is the corospond CartController function index:
function index() {
return request()->session()->get('cart');
}
Now the problem
But POST request via axios are rejected with 419 CSRF token missmatch.
Strangely enough, I can make a POST request via Insomnia. By first requesting the token from the server and then specifying _token in the body of the POST request.
> POST /api/v1/cart HTTP/1.1
> Host: localhost:8000
> User-Agent: insomnia/2021.5.3
> Cookie: laravel_session=ZzbpJsfvV22NjO3TB5C3ekiCIp7hlPAcaaZHTkfU; XSRF-TOKEN=eyJpdiI6IjhvVFpWMWtwMm0vU1RKNHB2VzdMdUE9PSIsInZhbHVlIjoiaDNEWTlhcTBRTWI0aXhkaU54Z3I5VndoWFg4Q1A1WmpJUDI1S2hFRGtiMUUwYUl0OEN6S0Q3RVFDZDJXSTNvdTllUXMzN0o1RU94blpqVTdpdTcxaTVvZjlwdFk2Z3djeldteHp0R3Q4UEVjMEovbGk2SHJnVXZmbzRBUzI1RHkiLCJtYWMiOiI2MDMyYzFjNWVjODY4NjRjMTk4NGNmMmI3Yjg4M2VjYzU5YzcwZGRkNDIxMTRiOTc1N2FkMmQ2NTc4YTA5MjhiIiwidGFnIjoiIn0%3D
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: application/json
> Content-Length: 293
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="product_id"
| 1
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="amount"
| 1
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="_token"
| YgKRkQc9D5GWOPiP8zqVEcoA4FuvESf02SSjy7U3
| --X-INSOMNIA-BOUNDARY--
One feature I absolutely don't understand why I work with the web middleweare in the api route. The api middleware would generate this 500 error message for me:
"message": "Session store not set on request.",
And here is my axios request:
axios.get('http://localhost:8000/api/get-token').then(response => {
const _token = response.data._token
const bodyFormData = new FormData()
bodyFormData.append('product_id', product.id)
bodyFormData.append('_token', _token)
const headers = {
'Content-Type': 'multipart/form-data',
'Access-Control-Allow-Credentials': true,
'_token': _token
}
axios({
method: "post",
url: 'cart',
data: bodyFormData,
headers: headers,
}).then((res) => {
console.log('res',res)
}).catch((err) => {console.log(err)})
});
This is really a long text. I hope I have given all the necessary information.
CodePudding user response:
- One feature I absolutely don't understand why I work with the web middleweare in the api route.
- It is because API routes do not have session enabled for the application, because it expects application to use Tokens, so in order to use sessions, you need to have "Web" middleware
Token mismatch error would be because generated/provided token through get-token endpoint wouldn't be the correct one
- Solution I see would be to add /cart route as an exception route for CSRFTokenMatch, to do that open App/Http/Middleware/VerifyCsrfToken.php file and add route in protected $except array. This is not an ideal thing to do but this will solve your problem for now and help you to move forward.
Another option to make your application secure is to use Laravel Sanctum and generate a token for every guest user (Using Guest Model), if you need help with that I can explain more, but its a little different scenario
CodePudding user response:
My first guess would simply be that you aren't sending cookies, and need to do
axios.defaults.withCredentials = true
or, more secure (as it limits withCredentials
to when you actually want to use it):
const axiosWithCredentials = axios.create({
withCredentials: true
});
axiosWithCredentials.get('http://localhost:8000/api/get-token').then(response => {
const _token = response.data._token
const bodyFormData = new FormData()
bodyFormData.append('product_id', product.id)
bodyFormData.append('_token', _token)
const headers = {
'Content-Type': 'multipart/form-data',
'Access-Control-Allow-Credentials': true,
'_token': _token
}
axiosWithCredentials({
method: "post",
url: 'cart',
data: bodyFormData,
headers: headers,
}).then((res) => {
console.log('res',res)
}).catch((err) => {console.log(err)})
});