I am trying to authenticate api calls in my laravel 5.7 application. I have installed Passport following the documentation and I think I have not missed anything. Every API call returns a 401 Unauthenticated.
Debugging I have found that the problem is that the csrf token encrypted in a cookie (once it is decrypted) doesn't match the csrf header from the request. I know it because I am dumping $this->validCsrf($token, $request) inside the getTokenViaCookie($request) method in vendor/laravel/passport/src/Guards/TokenGuard.php and it's value is false.
I cannot think what I am doing wrong.
In my layout.blade.php I add <meta name="csrf-token" content="{{ csrf_token() }}">
In Kernel.php I add \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class after \App\Http\Middleware\EncryptCookies::class
And in telescope I can see in the request that the x-xsrf-token header and the laravel_token cookie are there.
Any idea about what the problem can be or any way to debug it will be welcome.
I have done a deeper look into the code, I see that the token encrypted and saved into the cookie seems to be the session token. In passport/src/Http/Middleware/CreateFreshApiToken.php:
public function handle($request, Closure $next, $guard = null)
{
$this->guard = $guard;
$response = $next($request);
if ($this->shouldReceiveFreshToken($request, $response)) {
$response->withCookie($this->cookieFactory->make(
$request->user($this->guard)->getKey(), $request->session()->token()
));
}
return $response;
}
So it looks like my problem is that the token encrypted is the session one then it gets compared with the csrf header and of course the authentication fails.
I wondr if this is the normal behaviour and I am missing something Or is it wrong?
I found what the problem was.
Because I am using bootstrap-vue I stopped using bootstrap. What I didn't realized is that in the vue app when it requires ./bootstrap.js it is not only loading bootstrap, it is also setting some headers.
Related
I have a React project and a seperate Laravel project, which communicate with eachother. But whenever I send a POST-request from React to Laravel, it gives me a CSRF Token Mismatch. Now if the form was inside my Laravel project, I could just add the token as a field in my form, but it's a seperate project so I'm not sure.
I tried to add the token in resources/js/bootstrap.js, because I'm using Axios. I changed the code to:
import axios from 'axios';
window.axios = axios;
window.Laravel = {csrfToken: '{{ csrf_token() }}'};
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = window.Laravel.csrfToken;
But it still doesn't work, how can I handle this?
First of all csrf work with web.php by default. So when you write a post request route within the web.php route file then you'll face csrf token problem.
Solution 1:
Whenever you're writing Rest APIs for SPA's or mobile apps. You should use api.php route file. Because in the api.php route file, some layers are excluded due to their stateless behavior.
If you use api.php route file then you don't need to send csrf token in request headers.
Solution 2:
if you're writing API in web.php then you need to add that route in App\Http\Middleware\VerifyCsrfToken middleware except array, like following:
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
];
}
In this way, that route will not throw csrf related errors.
I hope that will helps you.
I have a problem with Sanctum and Postman that's related to this post: SPA Authentication Issues with Sanctum and Postman
I followed everything from the Laravel docs about Sanctum and configured it correctly. Then I followed this tutorial: https://blog.codecourse.com/laravel-sanctum-airlock-with-postman/
Everything is working except for POST requests. When I do login, it works. But then I have a collection without the pre-request Script and when I do a GET request to for example /user, it will return the logged in user.
But when I change the method to POST in Laravel and in Postman, I'll get a CSRF token mismatch error.
Does anybody knows what I have to do, to make POST requests working?
Route::middleware('auth:sanctum')->post('/user', function (Request $request) {
return $request->user();
});
I've been using sanctum in one of my e-commerce APIs and I've also followed the same tutorial you've linked in the question. It's hard to tell what's the actual problem in your case but it seems like that you're not sending the X-XSRF-TOKEN header in your POST requests.
The last paragraph in the above-mentioned tutorial, the writer shows how to hit the /logout route which is a POST route.
Remove this function in the controller
public function __construct()
{
$this->middleware('auth');
}
Or change it to
public function __construct()
{
$this->middleware('auth:sanctum');
}
Also, check your RouteServiceProvider and change your API route to
Route::prefix('api/v1')
->middleware('auth:sanctum')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
csrf_token is used to validate forms having method POST in laravel and token is created dynamically,
two thing you can do
First thing if you are writing api's you need to use https://<base_url>/api
and routes in routes/api.php, there you donot need csrf_token but make sure to use proper api authentication
Second just disable csrf token for those routes until you are testing on postman, once you successfully tested enable again, its provide security
disable like this
<?php namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
'submitMyForm/*',
];
}
I have a SPA app where I try to implement Sanctum's CSRF protection.
From docs:
To authenticate your SPA, your SPA's "login" page should first make a request to the /sanctum/csrf-cookie endpoint to initialize CSRF protection for the application
Right now I request CSRF token before I login
axios.get('/sanctum/csrf-cookie').then(response => {
// Login...
});
Should I request CSRF token before doing ANY post request in my application?
If yes, I basically need to request a CSRF token before routes like POST api/password_reset, POST api/tracking, POST api/register etc.
Or is there any way to tell Laravel Sanctum to only return 419 CSRF token mismatch errors for protected routes, ie. routes with auth:sanctum middleware?
EDIT:
Just wanted to make it clear that I don't have an issue with CSRF implementation in general. It works great after I have requested the CSRF token. Axios will add the token in all subsequent requests. My question is really about when to do the first request to CSRF token.
I have searched for something similar and I stumbled upon this; Laravel: How to Avoid TokenMismatchException CSRF Token on certain routes using a method
You can therefore kindly exclude the routes from being checked for CSRF token by adding the route path in $except array in VerifyCsrfToken class inside the app/Http/Middleware/VerifyCsrfToken.php like shown below;
protected $except = [
'/api/password_reset',
'/api/tracking',
'/api/register',
];
This can be seen also in Laravel Official Documentation Excluding URIs From CSRF Protection
I've done some googling and checked this answer but am still facing a 401 Unauthorized response when consuming my own api from my laravel app from a non logged in user.
I've followed the official docs and have installed passport.
I've also have this on every axios request:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Token is also defined on my boilerplate:
<meta name="csrf-token" content="{{ csrf_token() }}">
Why are the requests still unauthorised?
If your api route is wrapped in the auth:api middleware, the key word being auth, then what you're doing will be impossible unless your user is not first logged in.
You can remove the auth and just have the api middleware but this will not protect this endpoint from cross-site/unauthorised requests.
A solution could be to create a client credentials grant token and send it through to your front-end, although you'll have to store this somewhere.
Personally because it's your own api I'd just create a route or route group in its own file and require this file into both your web and api route files. Therefore you'll be able to consume it from your own web app with the csrf protection & if you try to access it elsewhere it will need the prepended api in the url https://website.com/api and an access token.
Okay then, do not use "auth:api" for routes that do not need authenticated users, Why then?, have you noticed the key word AUTH === authenticated on 'auth:api" middleware ? this means it requires an authenticated user or rather check for authentication first even before going to api check.
So what to do ?
If you happen to have made that every request should got under "auth:api" middleware check under Http/Kernel.php file like below
'api' => [
'throttle:60,1',
'bindings',
'auth:api'
],
Just remove it there and add this middleware on the routes/api.php file on the routes that needs authenticated user only like below
Route::post('/comments', CommentsController#post')->middleware('auth:api');
Then let routes which do not need authenticated users to be like so
Route::get('/comments', 'CommentsController#index);
I am trying to create API with laravel passport authentication now it is very easy to make API with laravel passport. I am using personal access token to create access token for API users. As my client and server are both one, but when token not provided in the API it redirect to login page create by laravel auth scaffold php artisan make:auth, I want to return json response with message "Token not provided."
https://laravel.com/docs/5.4/passport#personal-access-tokens
I am using auth:api in routes.
How to pass json response for API if token not provided?
I also encountered this same issue. To solve that redirection, you have to pass an extra parameter into the header of the request:
Accept application/json
Now, it will return JSON response instead of redirection to the login page.
So now with an invalid request, it will return JSON response like:
{"error":"Unauthenticated."}
But there is no easy way to customize the passport responses. I'm searching some standard way, but didn't get any yet.
I also encountered this same issue. To solve that redirection, you have to pass header of the request of json response. for that i made decision to make middleware as below
public function handle($request, Closure $next)
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
the above middleware will force the header to add json response, group the api collection as below
Route::group(['middleware' => 'json.response'], function () {
//your routes here
});