Laravel - Session store not set on request
PhpLaravelSessionSession CookiesPhp Problem Overview
I recently created a new Laravel project and was following along the guide on Authentication. When I visit either my login or register route, I get the following error:
ErrorException in Request.php line 775:
Session store not set on request. (View: C:\Users\Matthew\Documents\test\resources\views\auth\register.blade.php)
I haven't edited any core Laravel files, I've only created the views and added the routes to my routes.php file
// Authentication routes
Route::get('auth/login', ['uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);
Route::post('auth/login', ['uses' => 'Auth\AuthController@postLogin', 'as' => 'login']);
Route::get('auth/logout', ['uses' => 'Auth\AuthController@getLogout', 'as' => 'logout']);
// Registration routes
Route::get('auth/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']);
Route::post('auth/register', ['uses' => 'Auth\AuthController@postRegister', 'as' => 'login']);
I don't have much experience with Laravel, so please excuse my ignorance. I'm aware that there is another question asking this same thing, but neither of the answers seem to work for me. Thanks for reading!
Edit:
Here's my register.blade.php as requested.
@extends('partials.main')
@section('title', 'Test | Register')
@section('content')
<form method="POST" action="/auth/register">
{!! csrf_field() !!}
<div class="ui input">
<input type="text" name="name" value="{{ old('name') }}" placeholder="Username">
</div>
<div class="ui input">
<input type="email" name="email" value="{{ old('email') }}" placeholder="Email">
</div>
<div class="ui input">
<input type="password" name="password" placeholder="Password">
</div>
<div class="ui input">
<input type="password" name="password_confirmation"placeholder="Confirm Password">
</div>
<div>
<button class="ui primary button" type="submit">Register</button>
</div>
</form>
@endsection
Php Solutions
Solution 1 - Php
You'll need to use the web middleware if you need session state, CSRF protection, and more.
Route::group(['middleware' => ['web']], function () {
// your routes here
});
Solution 2 - Php
If adding your routes
inside the web middleware
doesn't work for any reason then try adding this to $middleware
into Kernel.php
protected $middleware = [
//...
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
Solution 3 - Php
In my case (using Laravel 5.3) adding only the following 2 middleware allowed me to access session data in my API routes:
\App\Http\Middleware\EncryptCookies::class
\Illuminate\Session\Middleware\StartSession::class
Whole declaration ($middlewareGroups
in Kernel.php):
'api' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
'throttle:60,1',
'bindings',
],
Solution 4 - Php
If Cas Bloem's answer does not apply (i.e. you've definitely got the web
middleware on the applicable route), you might want to check the order of middlewares in your HTTP Kernel.
The default order in Kernel.php
is this:
$middlewareGroups = [
'web' => [
\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,
],
];
Note that VerifyCsrfToken
comes after StartSession
. If you've got these in a different order, the dependency between them can also lead to the Session store not set on request.
exception.
Solution 5 - Php
A problem can be that you try to access you session inside of your controller's __constructor()
function.
From Laravel 5.3+ this is not possible anymore because it is not intended to work anyway, as stated in the upgrade guide.
> In previous versions of Laravel, you could access session variables or > the authenticated user in your controller's constructor. This was > never intended to be an explicit feature of the framework. In Laravel > 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
For more background information also read Taylor his response.
Workaround
If you still want to use this, you can dynamically create a middleware and run it in the constructor, as described in the upgrade guide:
> As an alternative, you may define a Closure based middleware directly
> in your controller's constructor. Before using this feature, make sure
> that your application is running Laravel 5.3.4 or above:
>
>
>
>
> namespace App\Http\Controllers;
>
> use App\User;
> use Illuminate\Support\Facades\Auth;
> use App\Http\Controllers\Controller;
>
> class ProjectController extends Controller
> {
> /**
> * All of the current user's projects.
> /
> protected $projects;
>
> /*
> * Create a new controller instance.
> *
> * @return void
> */
> public function __construct()
> {
> $this->middleware(function ($request, $next) {
> $this->projects = Auth::user()->projects;
>
> return $next($request);
> });
> }
> }
Solution 6 - Php
Laravel [5.4]
My solution was to use global session helper: session()
Its functionality is a little bit harder than $request->session().
writing:
session(['key'=>'value']);
pushing:
session()->push('key', $notification);
retrieving:
session('key');
Solution 7 - Php
In my case I added the following 4 lines to $middlewareGroups (in app/Http/Kernel.php):
'api' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
'throttle:60,1',
'bindings',
],
IMPORTANT: The 4 new lines must be added BEFORE 'throttle' and 'bindings'!
Otherwise a "CSRF token not match" error will rise. I've struggled in this for several hours just to find the order is important.
This allowed me to access session in my API. I also added VerifyCsrfToken as when cookies/sessions are involved, CSRF needs to be taken care of.
Solution 8 - Php
I'm using Laravel 7.x and this problem arose.. the following fixed it:
go to kernel.php
and add these 2 classes to protected $middleware
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
Solution 9 - Php
I was getting this error with Laravel Sanctum. I fixed it by adding \Illuminate\Session\Middleware\StartSession::class,
to the api
middleware group in Kernel.php, but I later figured out this "worked" because my authentication routes were added in api.php
instead of web.php
, so Laravel was using the wrong auth guard.
I moved these routes here into web.php
and then they started working properly with the AuthenticatesUsers.php
trait:
Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {
Route::post('register', 'Auth\RegisterController@register')->name('register');
Route::post('login', 'Auth\LoginController@login')->name('login');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::post('email/verify/{user}', 'Auth\VerificationController@verify')->name('verification.verify');
Route::post('email/resend', 'Auth\VerificationController@resend');
Route::post('oauth/{driver}', 'Auth\OAuthController@redirectToProvider')->name('oauth.redirect');
Route::get('oauth/{driver}/callback', 'Auth\OAuthController@handleProviderCallback')->name('oauth.callback');
});
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
I figured out the problem after I got another weird error about RequestGuard::logout()
does not exist.
It made me realize that my custom auth routes are calling methods from the AuthenticatesUsers trait, but I wasn't using Auth::routes()
to accomplish it. Then I realized Laravel uses the web guard by default and that means routes should be in routes/web.php
.
This is what my settings look like now with Sanctum and a decoupled Vue SPA app:
Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'throttle:60,1',
],
];
> Note: With Laravel Sanctum and same-domain Vue SPA, you use httpOnly cookies for session cookie, and remember me cookie, and unsecure cookie for CSRF, so you use the web
guard for auth, and every other protected, JSON-returning route should use auth:sanctum
middleware.
config/auth.php
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
Then you can have unit tests such as this, where critically, Auth::check()
, Auth::user()
, and Auth::logout()
work as expected with minimal config and maximal usage of AuthenticatesUsers
and RegistersUsers
traits.
Here are a couple of my login unit tests:
TestCase.php
/**
* Creates and/or returns the designated regular user for unit testing
*
* @return \App\User
*/
public function user() : User
{
$user = User::query()->firstWhere('email', '[email protected]');
if ($user) {
return $user;
}
// User::generate() is just a wrapper around User::create()
$user = User::generate('Test User', '[email protected]', self::AUTH_PASSWORD);
return $user;
}
/**
* Resets AuthManager state by logging out the user from all auth guards.
* This is used between unit tests to wipe cached auth state.
*
* @param array $guards
* @return void
*/
protected function resetAuth(array $guards = null) : void
{
$guards = $guards ?: array_keys(config('auth.guards'));
foreach ($guards as $guard) {
$guard = $this->app['auth']->guard($guard);
if ($guard instanceof SessionGuard) {
$guard->logout();
}
}
$protectedProperty = new \ReflectionProperty($this->app['auth'], 'guards');
$protectedProperty->setAccessible(true);
$protectedProperty->setValue($this->app['auth'], []);
}
LoginTest.php
protected $auth_guard = 'web';
/** @test */
public function it_can_login()
{
$user = $this->user();
$this->postJson(route('login'), ['email' => $user->email, 'password' => TestCase::AUTH_PASSWORD])
->assertStatus(200)
->assertJsonStructure([
'user' => [
...expectedUserFields,
],
]);
$this->assertEquals(Auth::check(), true);
$this->assertEquals(Auth::user()->email, $user->email);
$this->assertAuthenticated($this->auth_guard);
$this->assertAuthenticatedAs($user, $this->auth_guard);
$this->resetAuth();
}
/** @test */
public function it_can_logout()
{
$this->actingAs($this->user())
->postJson(route('logout'))
->assertStatus(204);
$this->assertGuest($this->auth_guard);
$this->resetAuth();
}
I overrided the registered
and authenticated
methods in the Laravel auth traits so that they return the user object instead of just the 204 OPTIONS:
public function authenticated(Request $request, User $user)
{
return response()->json([
'user' => $user,
]);
}
protected function registered(Request $request, User $user)
{
return response()->json([
'user' => $user,
]);
}
Look at the vendor code for the auth traits. You can use them untouched, plus those two above methods.
- vendor/laravel/ui/auth-backend/RegistersUsers.php
- vendor/laravel/ui/auth-backend/AuthenticatesUsers.php
Here is my Vue SPA's Vuex actions for login:
async login({ commit }, credentials) {
try {
const { data } = await axios.post(route('login'), {
...credentials,
remember: credentials.remember || undefined,
});
commit(FETCH_USER_SUCCESS, { user: data.user });
commit(LOGIN);
return commit(CLEAR_INTENDED_URL);
} catch (err) {
commit(LOGOUT);
throw new Error(`auth/login# Problem logging user in: ${err}.`);
}
},
async logout({ commit }) {
try {
await axios.post(route('logout'));
return commit(LOGOUT);
} catch (err) {
commit(LOGOUT);
throw new Error(`auth/logout# Problem logging user out: ${err}.`);
}
},
It took me over a week to get Laravel Sanctum + same-domain Vue SPA + auth unit tests all working up to my standard, so hopefully my answer here can help save others time in the future.
Solution 10 - Php
I had the same error but it was due to a misconfiguration in my sanctum.php file. Inside the block for the stateful Domains, I had still a development domain instead of my production domain.
'stateful' => explode(',', env(
'SANCTUM_STATEFUL_DOMAINS',
'mydomain.com')
),
It throwed the exact same error.
Hope that helps someone.
Cheers
Solution 11 - Php
Do you can use ->stateless()
before the ->redirect()
.
Then you dont need the session anymore.
Solution 12 - Php
Are you facing this issue when running a test for SPA authentication with sanctum?
Solution:
Add this line to tests/TestCase.php
:
public function setUp(): void
{
parent::setUp();
$this->withHeader('origin', config('app.url'));
}
Explanation:
In order to use sanctum for SPA auth, you have to add this to api middleware:
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class
EnsureFrontendRequestsAreStateful
class only works when its static method fromFrontend
returns true:
public static function fromFrontend($request)
{
$domain = $request->headers->get('referer') ?: $request->headers->get('origin');
if (is_null($domain)) {
return false;
}
// ... ommited
}
Session store is not set because $domain
is null when this method is running on unit tests.
Solution 13 - Php
in my case it was just to put return ; at the end of function where i have set session
Solution 14 - Php
If you are using CSRF enter 'before'=>'csrf'
In your case
Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);
For more details view Laravel 5 Documentation Security Protecting Routes
Solution 15 - Php
It's not on the laravel documentation, I have been an hour to achieve this:
My session didn't persist until i used the "save" method...
$request->session()->put('lang','en_EN');
$request->session()->save();
Solution 16 - Php
Laravel 5.3+ web middleware group is automatically applied to your routes/web.php file by the RouteServiceProvider.
Unless you modify kernel $middlewareGroups array in an unsupported order, probably you are trying to inject requests as a regular dependency from the constructor.
Use request as
public function show(Request $request){
}
instead of
public function __construct(Request $request){
}
Solution 17 - Php
In my own case (in Laravel 5.8) the session.php
config file in my Laravel project located in the ../[mylaravelapp]/config/
directory was missing. Hence, I copied the missing file from another Laravel project back to the config
directory, which fixed the problem.
Solution 18 - Php
Ok, here is what you are facing: You are calling a request association session which is not instantiated to the request you made. You only need to setLaravelSession on the request which will init session on request on which you want to have the session data available.
$request->setLaravelSession(session())
Solution 19 - Php
just add in top public/index.php
ob_start();
it's work with shared host
if not work try change this code in app/Http/Middleware/TrustProxies.php
protected $proxies;
to
protected $proxies = '*';