Laravel 5.5 MDN Fetch API set session variable not working - php

I'm using Laravel 5.5
I am trying to set session variable on GET fetch('/ajax-get-save-session') request.
Route::get('/ajax-get-save-session', function() { session(['my_key', 'Saved Session']) });
And when I try to access the session variable my_key in different route.
Route::get('/access-session', function() { return session('my_key'); });
NULL value is returned.
This routes are registered on Laravel web.php and I'm using MDN fetch API
Anyone?

If i understand your problem. You are looking for this. Try this:-
Use Session; //if your are using laravel 5.2 then add in top in routes. php or laravel 5.2 > greater versions in web.php
// now put data in session
Route::get('/ajax-get-save-session', function() {
Session::put('my_key','Saved Session');
});
//Access the session
Route::get('/access-session', function() { echo Session::get('my_key') });
Hope it helps!

It's totally my mistake. I never thought fetch() API is the one causing my problem. It says the description
By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).
By doing :
fetch(url, { credentials : 'include' }).then(response => response.json())
SOLVED MY PROBLEM!

Related

$this->request->getData() empty after upgrade to CakePHP 4.0

After upgrading to CakepPHP 4.0 my POST request trough XMLHttpRequest stopped passing data to $this->request->getData()
The data is accesible by $this->request->input('json_decode'); though and GET requests work as well.
But I wonder what has changed in comparision to 3.* and why it's not working like it was before.
This is my xhr:
this.$http.post(
url,
data,
{headers: {'X-CSRF-TOKEN': '<?= $this->request->getAttribute('csrfToken') ?>'}},
})
.then(response => {
//
}
);
It gives me an empty array when I call $this->request->getData()
I tried to turn off FormProtection component for that specific action but nothing changed.
If want to know what changed, check the out the migration guide first, in this case specifically the breaking changes section for components.
The request body parsing features have been removed from the request handler component (the deprecation warning that was in place previously has been removed too, as it was causing too many false positives). This should now be handled by the body parser middleware, which you need to add your application accordingly, either globally in your Application class':
public function middleware(MiddlewareQueue $middlwareQueue): MiddlewareQueue
{
// ...
$middlwareQueue->add(new \Cake\Http\Middleware\BodyParserMiddleware());
return $middlwareQueue;
}
or in a routing scope:
\Cake\Routing\Router::scope('/', function (\Cake\Routing\RouteBuilder $routes) {
$routes->registerMiddleware('bodyParser', new \Cake\Http\Middleware\BodyParserMiddleware());
$routes->applyMiddleware('bodyParser');
// ...
});
See also
Cookbook > Middleware > Using Middleware
Cookbook > Routing > Connecting Scoped Middleware

How to refresh the laravel_token on API calls with Passport

I created an SPA with Laravel 5.6, Vue 2.5 and Laravel Passport which is working quite well. I really love Laravel and Vue as they make building SPAs with APIs very easy and a lot of fun.
After setting up Laravel Passport as described in the docs the login as well as calls to the API are working as expected based on the 'laravel_token' which is correctly returned and stored in the cookie.
However, my problem is that my users are using the app for a pretty long time, without reloading the page but only performing calls against the API with axios. Somehow Laravel does not refresh the 'laravel_token' (and the corresponding cookie) in API calls (it does so, when I call a 'web' route). Consequently, the 'laravel_token' expires t some point and the user needs to log in again.
How can I force Laravel to refresh the 'laravel_token' (and thereby prolong its validity) with every call of an API route from axios?
Any help is very much appreciated!
I solved a similar issues in the past by creating a simple route (in the web middleware group) to keep the session alive for as long as the browser tab is open.
In routes/web.php:
Route::get('/keep-alive', function () {
return response()->json(['ok' => true]);
})->middleware('auth');
And then ping this route periodically with javascript:
setInterval(() => {
axios.get('/keep-alive')
.then(() => {})
.catch(() => {})
}, 600000)
I go into a little more detail here: https://stackoverflow.com/a/57290268/6038111
Axios has a way to "intercept" / see if a call failed. Inside the error callback I am seeing if it was an unauthenticated error and simply reloading the page.
Admittedly, I would love to be able to write another Axios call inside the error caught block to grab another session token "laravel_token", but have yet to find a way to do it. Reloading the page will refresh the laravel_token though, so it solves my problem for now. ¯\_(ツ)_/¯
After-thought: I'm actually thinking you probably couldn't refresh the laravel_token through an Axios call because you'e already dropped the session. I'm guessing you have to do it this way.
// Refresh Laravel Session for Axios
window.axios.interceptors.response.use(
function(response) {
// Call was successful, don't do anything special.
return response;
},
function(error) {
if (error.response.status === 401) {
// Reload the page to refresh the laravel_token cookie.
location.reload();
}
// If the error is not related to being Unauthorized, reject the promise.
return Promise.reject(error);
}
);

Laravel 5.4 null csrf_token() when posting to route

I'm sending an ajax post request, and with Laravel it seems that is done by creating a post route for it. I've set it up so a csrf token is put in the header automaticaly for every ajax request using ajaxSetup. I'm attempting to then catch that header on the backend and verify the tokens match.
In my web routes (which automatically use the web middleware), this returns as expected:
Route::get('/test', function() {
return csrf_token();
});
However, when I post to a route via AJAX, like either of the below ways:
Attempt 1:
Route::post('/test', 'AjaxController#test');
In the AjaxController construct, followed by an alert in the view:
var_dump(csrf_token().',hi'); die;
Response: ',hi' (csrf_token was null).
Attempt 2:
Route::post('/test', ['test' => csrf_token().',hi', 'uses' => 'AjaxController#test']);
$test = $request->route()->getAction()['test'];
var_dump($test); die;
Response: ',hi' (csrf_token was null).
What I seem to be running into is, with get requests csrf_token() is populated, on my post request, it is not.
Any ideas?
check your route group it must apply the web middleware as
Route::group(['middleware' => 'web'], function () {
Route::get('/test', function() {
return csrf_token();
//or return $request->session()->token();
});
});
Finally figured this out.
CSRF can indeed be checked on an ajax post request. I wanted to make sure someone on their own site isn't hitting my ajax endpoint with any success of doing anything, especially for another user.
However, I ran into a Laravel order of operations issue, with the way Laravel sets up the session. I was trying to call a validation method (within in the same class) in the constructor, where I validated for CSRF and verified the requesting user all in one place. I wanted to do this so that any time someone hits this class, I didn't have to call the verification in each public method in the class, I'd only have to call it once.
However, csrf_token(), and the request session in general, is not available to me yet in my construct. It is, however, available to me in the method within the controller class that is called in the route.
For example, given the following route:
Route::post('/test', 'AjaxController#test');
If I injected Request into the construct and then tried to reference anything in the session (in the construct), or get the value of csrf_token(), it will throw an error, because Laravel hasn't set that stuff up yet. But if I reference either of those things in the test method, it'll be there and available just fine.
A bit of a weird Laravel order of operations issue.
csrf protections are managed by Laravel Forms. It won't be available when dealing with APIs.
You should have a look at how middlewares are used in Laravel
https://laravel.com/docs/5.4/middleware
Think using API middleware for your APIs ;)
If you run this command php artisan make:auth documented here https://laravel.com/docs/5.4/authentication#authentication-quickstart when going to resources/views/layouts/app.blade.php you'll see this:
<meta name="csrf-token" content="{{ csrf_token() }}">
And in app.js
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')
}
});
In 5.3 there was this cool feature which looks as though it has since been removed in 5.4.
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
So what you need to do is add the csrf field to every request. Do the first 2 code snippets and you'll be fine. The 3rd I believe is probably for Vue.
Answer to your question: no, no, no and no. CSRF tokens I wouldn't believe are generated in POST requests, it's a Cross site Reference token, not an authentication token. If you're looking for something like authentication token refreshing then checkout JWT although the packages for JWT for laravel are a bit unfinished at the moment; with a little work you can get them working.
https://github.com/tymondesigns/jwt-auth 1.0.*#dev is pretty good. You can then use their refresh middleware to generate new tokens on request but this is quite advanced and unless it's for authentication then I wouldn't bother really.
I believe Dingo (another work in progress I believe) https://github.com/dingo/api uses the above package
Anything else let me know!

Laravel 5.2 : Session values doesn't persist across different routes?

I'm using Laravel 5.2. (And i'm a new Laravel user)
I need to use Session. And of course, read the Session Values from different Routes/pages. Here is the simple concept, simply:
Save some session values from a route.
Retrieve the values from another different route.
Here's the code i used.
Route::get('write', function () {
session()->put('food', 'banana');
session()->save();
echo session()->get('food'); // <------ Shows: 'banana'
});
Route::get('read', function () {
echo session()->get('food'); // <------ Shows nothing*
});
What did i miss (or) what is the proper way to play with Sessions please?
Thanks all.
The reason is that session()->pull() actually pulls an element from the session and deletes it from session. So after /write called you'll have nothing in you session. That's why /read can not pull anything.
You should use session()->get('food') in your scenario instead of pull.
Also make sure that your routes use web middleware.
Route::group(['middleware' => ['web']], function () {
Route::get('write', function () {
session()->put('food', 'banana');
echo session()->get('food'); // <------ Shows: 'banana'
});
Route::get('read', function () {
echo session()->get('food'); // <------ Shows 'banana' too
});
});
Also check the official documentation for further read: https://laravel.com/docs/5.2/session.
use session()->save() after you put or modify something in it.
I usually do it via the facade Session::save();
If it isn't saved the session object wont know about it.

Laravel 5 doesn't store session variable between pages [duplicate]

I have a brand new installation of Laravel 5, in fact I have tried this on multiple versions and keep hitting the same issue.
I have not changed anything from the default except setting the session driver to redis. (File based also has the same issue).
I have two routes set as follows
Route::get('/set/{value}', function($value) {
var_dump(Session::getId());
Session::set('test', $value);
return view('welcome');
});
Route::get('/get', function() {
return 'Get ' . Session::get('test');
});
If I visit the url /set/abc I see the session appear in REDIS (I also see the file created when using file based). The session looks fine in REDIS as shown below
127.0.0.1:6379> KEYS *
1) "laravel:1a3ae6caff6346e4a173fdc1ab4c6eb0f138806b"
2) "laravel:fed1af2fb44c6e625953237c3fa6fcbb05366a5c"
3) "laravel:cb37286ccfe3e7caa20557aca840f50cb5a5f20d"
Every time I visit the page though, it recreates a new session.
The key parts of session.php file is as follows:
'lifetime' => 120,
'expire_on_close' => false,
I have also checked in REDIS the TTL of the session variables and they do get initialised at 120 minutes (equivalent in seconds).
Any idea what I am doing wrong?
It might be worth noting I am using a homestead vm (completely stock) to test this. I have also tried using multiple browsers. No cookies are ever sent to the browser, I presume a session id should be sent to the browser as part of the initial get request?
Laravel's middleware class \Illuminate\Session\Middleware\StartSession is responsible for starting your session. Before L5.2, this ran on every request because it was part of the global middleware stack. Now, it's optional because L5.2 wants to allow for both a web UI and an API within the same application.
If you open up app/Http/Kernel.php, you'll see that the StartSession middleware is part of a middleware group called web. You need to put all your routes inside there for your example to work.
Route::group(['middleware' => ['web']], function () {
Route::get('/set/{value}', function($value) {
var_dump(Session::getId());
Session::set('test', $value);
return view('welcome');
});
Route::get('/get', function() {
return 'Get ' . Session::get('test');
});
});
You can see that the web middleware group is also responsible for other things like providing the $errors variable on all views.
You can read more about it in the docs:
By default, the routes.php file contains a single route as well as a route group that applies the web middleware group to all routes it contains. This middleware group provides session state and CSRF protection to routes.
Any routes not placed within the web middleware group will not have access to sessions and CSRF protection, so make sure any routes that need these features are placed within the group. Typically, you will place most of your routes within this group:
Source: https://laravel.com/docs/5.2/routing

Categories