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

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

Related

Laravel 5.6 - How to authenticate API using sessions for same folder SPA?

I have a React SPA in the same Laravel project. The login/signup/logout and all other js views are in the js folder and use axios api calls for all POST/GET requests. I want to use the default Laravel session based web authentication for the embedded SPA, since it's in the same project folder and it will be the only javascript client accessing it. This api does not need to be open to the public, just for this react app, and it's an SPA for the speed and good user experience instead of full page reloads.
I've tried using Passport before, and for over a month, I still can't get it to work as intended. I do not want to deal with tokens, access tokens, refresh tokens, revoking tokens, CSRF, etc. Just the out of the box simple Laravel session based auth that works so easily on web, but want it to work on my react app. The only blade file is the index.blade.php which includes the react app.js
Any idea how we can accomplish this?
UPDATE 1:
After implementing #ceejayoz's suggestion:
You have to add the various Session/Cookie middlewares in
app/Http/Kernel.php (stuff like
\Illuminate\Session\Middleware\StartSession::class) to the API routes.
I added to $middlewareGroups.api to match the web middleware in app/Http/Kernel.php:
'api' => [
'throttle:60,1',
'bindings',
// Newly added middleware to match web middleware
\App\Http\Middleware\EncryptCookies::class
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
I realized there are two issues that occurred:
In the sessions table, even if not logged in, when loading app home page (or any page), multiple sessions are inserted into the sessions table. Shouldn't a new single session be inserted into this table only after user login?
After user log in, when refreshing the page manually in the browser and a call is made to a protected route, I get a 401 Unauthenticated which points me to this method in Illuminate/Auth/GuardHelpers.php:
public function authenticate() {
if (! is_null($user = $this->user())) {
return $user;
}
throw new AuthenticationException; // throws this 401 exception on logged in page refresh when fetching data from private route
}
Some additional notes:
In config/auth.php I updated the guards.api.driver to session instead of token.
In routes/api.php I have the protected routes wrapped in auth middleware like this: Route::group(['middleware' => 'auth'], function() { PRIVATE ROUTES HERE }
In config/session.php I have 'domain' => '.mydomain.com'
I am sending back these headers with each axios api request like this:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
Any idea how we can fix these 2 issues?
Looks like your session was not persistent.
Check if you changed any values in config/session.php that might create problems.
You can check default sesion config values here
From the comments, #Wonka solved his problem by changing
'same_site' => 'strict'
to
'same_site' => null
It's doable (and I've done the same myself for some apps).
By default, the routes in routes/api.php don't have sessions available, but you can add the various Session/Cookie middlewares in app/Http/Kernel.php (stuff like \Illuminate\Session\Middleware\StartSession::class) to the API routes.
You can, as #ljubadr suggested, also put the API routes right in routes/web.php instead, although that'd probably mean you'd need to make other changes (like removing CSRF protection from the web routes).

Laravel package session variable not saving with ajax call

I'm building a package called under-construction. When this package is activated in a config
file the site will be underconstruction only people with the right code can access the
application.
https://github.com/larsjanssen6/underconstruction
The problem that I have right now:
When the code is entered I make an ajax call that hit's this controller method (called check):
https://github.com/larsjanssen6/underconstruction/blob/master/src/Controllers/CodeController.php
If the code is correct a session variable is being set:
session(['can_visit' => true]);
Then in my vue.js code I redirect to /. And it will hit my middleware again. Here I check if a session called can_visit exists.
return session()->has('can_visit');
https://github.com/larsjanssen6/underconstruction/blob/master/src/UnderConstruction.php
But the session variable can_visit is always gone! How is that possible?
Thanks for your time.
You're not loading the session middleware, so session is not started and no values are persisted.
As was mentioned in the comments, even though your protected routes (/) are within the web middleware (read session), your service provider's routes (/under/construction, /under/check) are not (no write session).
The simple fix is to add the session, or even better, the whole web middleware.
$routeConfig = [
'namespace' => 'LarsJanssen\UnderConstruction\Controllers',
'prefix' => 'under',
'middleware' => [
'web', // add this
// DebugbarEnabled::class, // leaving this dead code behind despite vcs
],
];
However, you might quickly run into trouble with infinite redirect loops if a user adds your middleware to their web middleware group. So I would add a check of some sort to make sure you're not on one of the existing underconstruction routes.
public function handle($request, Closure $next)
{
// check this isn't one of our routes
// too bad router hasn't loaded named routes at this stage in pipeline yet :(
// let's hope it doesn't conflict with user's routes
if ($request->is('under/*')) {
return $next($request);
}
if (! $this->config['enabled']) {
return $next($request);
}
if (!$this->hasAccess($request)) {
return new RedirectResponse('/under/construction');
}
return $next($request);
}
And ultimately guessing from the context of this project, I'd expect most people would want to stick this in the global middleware. However, you're going to run into the same session-hasn't-started-yet issues because that doesn't run in the global middleware. So there's more to chew on. Happy coding!

Laravel 5.2 session not persisting

Lately I've been working on a project in Laravel 5.2 and now I'm having problems with sessions not persisting. I've read most of the questions already asked regarding this but everyone has the same answer that I have already tried - applying web middleware.
I've read that there was a new L5.2 update where the web middleware group is already applied by default. I checked my routes with php artisan route:list and I can see that every route has only 1 web middleware applied.
I'm creating session with $request->session()->put('key', 'value') but as soon as I comment that line the session is nowhere to be seen anymore.
Edit
I want to set the session inside a controller when I visit a news page, but I tried it on a simple test route as well. Route where I set this is news/{id} and I want to use it on the front page which is in /
I wish to store recently visited pages in session so I can then show it to the user on the front page.
Session config file I left untouched. So it's using file driver
Here is a tested routes to use for your projects
Please use a middleware instead of the function in the routes file
routes.php
// Only as a demo
// Use a middleware instead
function addToSession ($routeName) {
$visited = session()->get('visited', []);
array_push($visited, $routeName);
session()->put('visited', $visited);
}
Route::get('/', function () {
addToSession('/');
return view('welcome');
});
Route::get('/second', function () {
addToSession('/second');
return view('welcome');
});
Route::get('/third', function () {
addToSession('/third');
return view('welcome');
});
Route::get('/history', function() {
return session()->get('visited');
});
The /history route will return a JSON having the history.

Laravel 5 and OneLogin [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

laravel 5.2 authentication - Missing Links

I am new to laravel framework and trying to build up authentication for a website. There is something really strange thats happening and I am not able to figure out whats wrong.
I issue php artisan make:auth command and I could see the corresponding files getting generated under controllers and the resources/views. I am able to login and see the homepage (after login). I am able to logout as well and everything works smoothly so far.
now sometimes there seems to be a problem when I am away from the browser for sometime, and come back to the website, it starts acting wierd. the app loses the information about the current logged in user. If I go to the home page (the actual homepage of the website and not the page after the login), then the login page ("/login") does not show up. I have to manually logout (by typing "/logout" in the url) and then try the login url to see the login form.
this is my routes file:
Route::get("/", "PagesController#home")->name("home");
Route::get("/search/{query}","APIController#index")->name("search");
Route::get("/searchBook/{id}","APIController#searchBook")->name("searchBook");
Route::get("/stories","PagesController#stories")->name("stories");
Route::get("/user/{id}/deleteBooks/{book_id}","UserController#deleteBooks")->name('user.delete.books');
Route::get("/user/{id}/showBooks/{book_id}","UserController#showBooks")->name('user.show.books');
Route::group(['middleware' => 'web'], function () {
Route::auth();
Route::resource('user', 'UserController');
Route::get('/user/{user}/books',"UserController#books")->name('user.get.books');
Route::post("/user/{user}/createBooks","UserController#createBooks")->name('user.create.books');
Route::get('/home', 'PagesController#dashboard')->name("dashboard");
Route::post("/savemap","UserController#savemap")->name("savemap");
});
Also, It seems the app in itself is not really taking care of the authentication. I manually have to check the authentication (by Auth::check()) at lot of steps and it is painful. For example at many places I have to manually do
if (Auth::check()) {
// some code
}
else{
Auth::logout();
return redirect()->route('home'); //named route
}
This is an update : A route which was giving me issues was not placed under the web middleware in the routes.php file. So when I placed the concerned route under the web middleware, I was actually able to access all the Auth:: parameters and the current logged in user.
Does this mean that I have to place all my "logged-in" routes (available routes after logging in) inside the web middleware? and what about the /login, /logout routes... Should they be places any middleware?
Any route you need sessions (which Auth uses) needs to have the 'web' middleware group applied.
If you want to do auth checks you can use the 'auth' middleware which will do those checks for you.
Example:
Route::group(['middleware' => ['web', 'auth']], function() {
Route::get('mustbeauthed', 'SomeController#someMethod');
});
In this case going to the 'mustbeauthed' will redirect you away if you are not authenticated and let you pass through to it if you are authenticated.

Categories