167

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
mattrick
  • 3,580
  • 6
  • 27
  • 43

22 Answers22

228

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
});
Cas Bloem
  • 4,846
  • 2
  • 24
  • 23
89

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,
];
Waiyl Karim
  • 2,810
  • 21
  • 25
68

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',
        ],
George Kagan
  • 5,913
  • 8
  • 46
  • 50
31

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.

MHG
  • 1,410
  • 3
  • 19
  • 32
  • i have it exactly like that. í still get the message. I also tried putting StartSession and ShareErrorsFromSession in the $middleware array. Storate/frameword is also writeable. (I'm using Wampserver 3 btw.) – Meddie Mar 10 '16 at 09:23
  • use 'middleware' => ['web','youanother.log'], – Kamaro Nov 29 '16 at 12:12
  • 3
    Yep! I was dumb and thought I'd reorder those alphabetically (because OCD) and that broke the app. Unfortunately, I didn't test until the next day, which is why I ended up here. Just for the record, the default order for the "web" middleware group in 5.3 is: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, SubstituteBindings, VerifyCsrfToken. – Ixalmida Dec 07 '16 at 14:53
25

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:

<?php

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);
        });
    }
}
Daan
  • 7,685
  • 5
  • 43
  • 52
  • 1
    This was my issue, I was trying to access a session variable inside the __constructor() method. – GTS Joe Apr 12 '21 at 21:21
19

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');
izzaki
  • 258
  • 2
  • 4
  • This is not working when we write session variable in a controller and uses in another controller :( – Kamlesh Apr 20 '19 at 12:41
  • This also worked for me. I had layers of issues. I "fixed" the problem with the Kernel suggestion, only to later discover I broke sessions by adding StartSession to the wrong Middleware in Kernel.php — once I removed that, I ultimately found $request->session() was problematic in my custom middleware class and it fixed the issue when I switched to session() only. – JessycaFrederick Apr 27 '22 at 16:15
13

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,
dazed-and-confused
  • 1,293
  • 2
  • 11
  • 19
Aseil Hamid
  • 131
  • 1
  • 2
  • this has worked for me in the latest laravel 9 as of June 2022 – NMukama Jun 07 '22 at 10:05
  • But why? It should not require that. Is anyone able to explain it? – Robert Jul 10 '22 at 17:48
  • Well, this is in fact an anti-pattern for an API. The main idea of using JWT is to have a Sessionless Authentication. By doing this you will be creating a session for your user – Luciano Sep 07 '22 at 22:42
  • This saved my bacon, I was building a unit test with a route that had no middleware group, didn't occur to me that I'd have to manually add StartSession to it to unit test sessions. Thanks! – Jeremy Wadhams Jul 28 '23 at 16:47
10

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.

Marinne
  • 331
  • 3
  • 5
  • If you are writing apis with laravel this is the answer your looking for :) or add [->stateless()->redirect()](https://laravel.com/docs/7.x/socialite#stateless-authentication) – Bobby Axe May 22 '20 at 02:34
4

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', 'test-user@example.com');

    if ($user) {
        return $user;
    }

    // User::generate() is just a wrapper around User::create()
    $user = User::generate('Test User', 'test-user@example.com', 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.

agm1984
  • 15,500
  • 6
  • 89
  • 113
  • Do i need to use login if I want user's authenticated? I do not have login since jwt is authenticated via proxy. I just want to set session value to authenticated so that subsequent requests do not need to auth with 3rd party again. Getting " Session store not set on request.".. – trainoasis Dec 05 '20 at 11:44
  • If you make a question and link it here, it will create a nice follow-reference for someone else, and I might be able to help; but it's been a while since I was looking at auth code. `Session store not set on request` might relate directly to `StartSession::class`. Something upstream of that might not be getting set properly, or your usage of `web` and/or `api` isn't aligned properly. – agm1984 Dec 05 '20 at 19:22
4

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

Taranis
  • 314
  • 1
  • 2
  • 11
  • 1
    This was my issue - a note to future viewers and future me: you can set wildcards in `SANCTUM_STATEFUL_DOMAINS` such as `*.myproject.test` – Carwyn Stephen Apr 17 '22 at 22:48
3

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.

glinda93
  • 7,659
  • 5
  • 40
  • 78
2

Do you can use ->stateless() before the ->redirect(). Then you dont need the session anymore.

1

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())
ouflak
  • 2,458
  • 10
  • 44
  • 49
0

in my case it was just to put return ; at the end of function where i have set session

0

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

0

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();
gtamborero
  • 2,898
  • 27
  • 28
0

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){

}
Kanchana Randika
  • 550
  • 2
  • 12
  • 27
0

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.

user28864
  • 3,375
  • 1
  • 25
  • 19
0

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 = '*';
Waad Mawlood
  • 727
  • 6
  • 10
0

The following are working for me...

Making session when user is logging in

$r->session()->put('usrSession',$r->uName);

Here 'usrSession' is my key and 'uName' is coming from request's variable (actually its the unique username of that user)

Flushing (all) sessions when user is logging out

$r->session()->flush();
0

Some note could help you as helped me:

As this post says, dd('') function has some conflicts with session feature (and of course maybe with some other features like cookies, ... I didn't test);

The resone is unknown for me! maybe dd() clears buffer.

So just DON'T use dd() in combination with session.

Amin.Qarabaqi
  • 661
  • 7
  • 19
0

you should add the domain address of your spa to your config/sanctum.php file at the 'stateful' section

Drine
  • 1
  • 1
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 20 '23 at 11:41