Laravel: CSRF token not needed for login via axios - laravel

When I use a form to post to the /login route, a valid CSRF token is needed.
<form role="form" method="POST" action="/login">
However, I can login completely without the token simply using:
axios.post("/login", { email:this.email, password:this.password })
.then((res) => {
window.location.href = "/dashboard";
})
This is a security risk, right? Why is that? How can I fix it?

It's not a vulnerability. If you check your resources/js/bootstrap.js file, a comment explains this.
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
If you inspect the request made in the network tab of your browser's dev tools, you will see the XSRF-Token header.

just add this in your frontend.blade.php file head:
    
<meta name="csrf-token" content="{{ csrf_token() }}">
And in your bootstrap.js file it is
well protected by declaring and authorizing axios.

Related

Laravel CSRF Token Mismatch Post request from seperate React project

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.

How Laravel middleware auth:api detect token from cookie?

I just trying to makes my auth flow more secure using a cookie on Laravel 5.7
Here my code
/**
* auth logic
*/
return response()->json(["status" => "logged in"], 200)->cookie('token', $token, $lifetime);
Then the cookie will be saved on the browser and will be used on every request.
On header with Axios
"cookie":"token={token}"
And I validate the auth using default middleware
Route::group(['middleware' => ['auth:api']])
But the auth:api did not recognize it, I can make custom middleware by manually detect the cookie, but I can't use the auth()->user() function on it.
Is there any solution for this?
From your sample code I believe your app is built on a stateless architecture where you have your JavaScript client and laravel api.
Now I am a bit confused as to why you do not want the client storing the token, if you just want to escape cross site scripting vulnerability (XSS) then you have to prepare to deal with cross site request forgery (CSRF) if you store the token in the browsers cookie.
Regarding the middleware not being able to find the token, by default the middleware is configured to lookup tokens in the request header (specifically the Authorization header) so if you decide to store it in the cookie, you have to find a way to change the token lookup in the api middleware which unfortunately I have not done before in laravel.
APIs don't generally store and send cookies. Therefore the api token authentication guard will not look for the token in a cookie. There are multiple options you can send it as though the easiest one in axios:
{
headers: {
Authorization: `Bearer ${token}`
}
}

Vue cookie-based authentication in Laravel

From reading the Laravel docs, it appears that when using Sanctum Vue will just use the cookie-based authentication.
I was trying to move an existing app (with login done using Livewire) to Vue, but calls direct to my first api endpoint were redirecting to the login page.
So I created a clean installation of Larvel, installed Breeze (with Inertia), then Sanctum, published the config etc.
But when I login and then visit my test endpoint (which just returns 200 Ok), it redirects to the login page (which, because I am logged in, redirects to the Breeze dashboard).
Do I need to do anything manually for my endpoint, guarded by auth:sanctum, to pass authentication?
Update
I've set axios.defaults.withCredentials, and it's returning 401 Unauthorized. My app.js:
axios.defaults.withCredentials = true;
axios.get('/api/test')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
First ensure that your application's CORS configuration is returning the Access-Control-Allow-Credentials header with a value of True. This may be accomplished by setting the supports_credentials option within your application's config/cors.php configuration file to true.
Then, if you are using axios, you should enable the withCredentials option on your axios instance:
axios.get('some api url', {withCredentials: true});
or globally:
axios.defaults.withCredentials = true;
If you are not using Axios to make HTTP requests from your frontend, you should perform the equivalent configuration on your own HTTP client.
Laravel Docs Reference.
If you are using Postman to test your api, here is a smart implementation of sanctum authentication requests. Basically you have to get the sanctum cookie first, and then send the cookie on XSRF-TOKEN header.

Laravel Sanctum CSRF returns 419 for unprotected routes

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

Laravel how to post data to a protected route using axios and vue

Until now I have only used my Laravel app as a backend API to my mobile app. The user logs in and get a token that then is stored on the device, then the device use that basic http token on each request to my backend API.
But now I am building a web based version of my app. I want to send a POST update in my page using axios and vue, but I am not sure how I should do this since my route is protected by auth.
Should I do something like this?:
<your-component :auth_user="{{auth()->user()->api_token}}"></your-component>
Or simply create a meta:
<meta name="token" id="token" value="{{ auth()->check() ? auth()->user()->api_token : null }}">
This way my component gets my users api_token which can later be used in axsios when I send a post request using a basic http auth.
Or what is the common way to talk to a protected API using axios?
thanks
Laravel have a good package for API authentication called Passport
, so after configured, it create the routes for require and return the token. To request it's http://{domain}/oauth/token.
When the user try to log in, Vue must send a post request with axios passing the user data. If the user have access, the token it's returned.
To protect your routes you can use middleware('auth:api'). Passport uses this middleware to validate the token.
ie:
<script>
userData = {
...
},
axios.post('http://{domain}/oauth/token', userData) {
.then(function (response) {
console.log(response.access_token)
... redirect to dashboard or something else
}
}
...
</script>
As you may know, the token has to be passed in every client request, and a way to do this is passing the token in the HTTP request header. Fortunately Passport already do this for you.
Hope it helps.

Resources