Framework: [5.3] No access to sessions or auth on custom http error pages

Created on 7 Jan 2017  路  31Comments  路  Source: laravel/framework

  • Laravel Version: 5.3.28
  • PHP Version: 7.0.8-3ubuntu3 (cli)
  • Database Driver & Version: not relevant

Description:

When creating custom Blade views for displaying them to the users when a HTTP error occurs, I cannot access authentication or session variables inside. I'm aware that this happens because the standard "web" middleware does not apply to custom error pages, but I'm not sure if this is intentional. In comparison, the middleware that is applied on every request is also applied on custom pages.

If the behavior is intentional: Is there any way to apply middleware to the error pages, but not calling it on every request?

Steps To Reproduce:

  1. Create a custom error page, e.g. 404.blade.php and save it under views/errors/
  2. Try to access the Auth facade in side (e.g. just output another text on Auth::check();)
  3. Open up the page by accessing a not-defined URL

Related:

http://stackoverflow.com/questions/41517012/laravel-5-3-use-auth-middleware-on-custom-error-page

Most helpful comment

@manniL correct, There's also another option. This is how I fixed it without jQuery:

I created a custom StartSession Class in App\Http\Middleware

<?php

namespace App\Http\Middleware;

use \Illuminate\Session\Middleware\StartSession as BaseStartSession;
use \Illuminate\Http\Request;
use Closure;

class StartSession extends BaseStartSession
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if($request->url() == "api/*"){
            return $next($request);
        }

        return parent::handle($request, $next);
    }
}

Notice the way it ignores API Routes.

Then I added that to the global middleware in kernal.php and removed the old startsession from web

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\StartSession::class,
    ];

Next I created a class called SessionServiceProvider.php in App\Providers

<?php

namespace App\Providers;

use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
use App\Http\Middleware\StartSession;
class SessionServiceProvider extends BaseSessionServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton(StartSession::class);
    }
}

Then finally in app.php replace Illuminate\Session\SessionServiceProvider::class with App\Providers\SessionServiceProvider::class

I recommend doing composer dump-autoload and php artisan optimize in case it doesn't work

All 31 comments

Global middlewares are executed on a 404, but route-based middlewares aren't. That's because a 404 does not match a route, so there's no knowledge about which middlewares should execute. You would probably need to modify your exception handler to call the relevant middlewares (at least EncryptCookies and StartSession) when needed.

@sisve Thanks for the reply! Yes, I forgot to mention that global middleware is executed indeed. I've linked my question on StackOverflow which relates to the same topic. I've already thought about setting the StartSession middleware as a global one, but there should be a better solution.

Do you have an approach for modifying the exception handler for this particular case?

Having the same problem. I'll post if I find a solution, if you do please let me know

This is the intended behaviour. You can't expect laravel to guess whether a root mismatch wants sessions, because then 404s on your api would also start sessions.

Well how can we do Auth::check() in error pages, I'm sure this would be useful for many people?

I've already thought about setting the StartSession middleware as a global one, but there should be a better solution.

That's the perfect solution if you want sessions across your whole application. Wouldn't make sense to not do that actually. :)


Something I do on one of my apps is actually send an ajax request from the error pages in order to query the session. That's how the login button on StyleCI is able to show up on error pages.

image

image

Well how can we do Auth::check() in error pages, I'm sure this would be useful for many people?

You can't, but you can do the solution I just posted. :)

Could you give us an example please?

That's the perfect solution if you want sessions across your whole application. Wouldn't make sense to not do that actually. :)

What about API's? we don't want session middleware in them do we?

What about API's? we don't want session middleware in them do we?

Exactly, so you can't have the middleware on 404s, because they might not be web 404s.

Could you give us an example please?

I already posted the example. I'm not going to write your whole app for you I'm afraid. My description leaves it easy to implement yourself.

@GrahamCampbell Thanks for clarifying the problem and providing an example! Just to make sure that my understanding is correct:

  • When no API is used, it is a perfect solution to set the StartSession middleware as a global one
  • Otherwise, just query the corresponding HTML segments via jQuery as you did on the StyleCI page

Correct?

@manniL correct, There's also another option. This is how I fixed it without jQuery:

I created a custom StartSession Class in App\Http\Middleware

<?php

namespace App\Http\Middleware;

use \Illuminate\Session\Middleware\StartSession as BaseStartSession;
use \Illuminate\Http\Request;
use Closure;

class StartSession extends BaseStartSession
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if($request->url() == "api/*"){
            return $next($request);
        }

        return parent::handle($request, $next);
    }
}

Notice the way it ignores API Routes.

Then I added that to the global middleware in kernal.php and removed the old startsession from web

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\StartSession::class,
    ];

Next I created a class called SessionServiceProvider.php in App\Providers

<?php

namespace App\Providers;

use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
use App\Http\Middleware\StartSession;
class SessionServiceProvider extends BaseSessionServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton(StartSession::class);
    }
}

Then finally in app.php replace Illuminate\Session\SessionServiceProvider::class with App\Providers\SessionServiceProvider::class

I recommend doing composer dump-autoload and php artisan optimize in case it doesn't work

@MrMashy I followed the steps you mentioned and also run the last two commands, still can't access the auth() object.

Correct me if I'm wrong, but when you said "app.php" I updated "configapp.php" right?

@cjwotbot Yes correct, I'm not sure sure what the problem might me. Show me how you're accessing the auth object.

I use auth()->user() when accessing the auth user object. And if I'm testing it with blade, I use @if(auth()->user())

You should be using \Auth::User();

I was told to use auth() because we use Auth0 for our authentication.

Anyways, I'll retract my earlier statement when I said it wasn't working, apparently it works now. Thanks!

@cjwotbot Ok great, no problem, Glad it helped.

@MrMashy - Does the work on 5.4? I attempted your changes and am still not able to check the guard on a 500.blade.php custom view

@MrMashy - Does the work on 5.4? I attempted your changes and am still not able to check the guard on a 500.blade.php custom view

I am seeing the same thing in 5.4.27 even when I move ALL middleware to global.

Still not working in 5.4 even if i move this middlewares in global middlewares:

            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,

For those looking for an alternative, non-AJAX solution to this; What I did is create a new middleware group for web-rendered errors in the kernel which are then loaded in dynamically upon error rendering.
Implementation details of this can be seen in this commit (Only Handler.php and Kernel.php changes are relevant).

@GrahamCampbell what happens when you have custom layouts based on a session parameter? I have a middleware which determines the partner based on the sub-domain partnerx.domain.com and a layout specific to that partner is loaded.

Is there not a way we could load a partner specific error page? I have been including other layout files based on the value of the {{ session('partner')['username'] }} variable but in 404 error pages we cannot have access to the session since I cannot add it to the global middleware without affecting our API layer.

I understand an ajax request works in your case but I do not think that is possible for our use case, is there any other way to inject the middleware into the response once it is determined that the error is a web error and not an api error?

@ssddanbrown will that only work on web requests or will that also work for api requests?

@ssddanbrown I tried to implement this with Laravel 5.3 however it would appear that the method getMiddlewareGroups is not part of the route, any idea how to do this with 5.3?

The @MrMashy's approach solved the issue!

Thank you!

PS: I'm using v5.5

@DominusVilicus the approach solved the issue in Laravel 5.6 with php artisan clear-compiled command. But still have a problem with geting the route name: href="{{ route('pages.index') }}"

ErrorException (E_ERROR)
Call to a member function getName() on null

I've already thought about setting the StartSession middleware as a global one, but there should be a better solution.

That's the perfect solution if you want sessions across your whole application. Wouldn't make sense to not do that actually. :)

Something I do on one of my apps is actually send an ajax request from the error pages in order to query the session. That's how the login button on StyleCI is able to show up on error pages.

image

image

Sending AJAX request on 404 to trigger the session? This is nuts!

If it is still useful to someone, the solution to my problem:

app/Exceptions/Handler.php

    public function __construct(
        Container $container,
        StartSession $startSession,
        ShareErrorsFromSession $errorsFromSession
    ) {
        $this->startSession = $startSession;
        $this->errorsFromSession = $errorsFromSession;
        parent::__construct($container);
    }

    public function render($request, Exception $e)
    {
        if (null === $request->getSession()) {
            return $this->startSession->handle($request, function($request) use ($e) {
                return $this->errorsFromSession->handle($request, function ($request) use ($e) {
                    return parent::render($request, $e);
                });
            });
        }
        return parent::render($request, $e);
    }


//=======================
Voc锚 pode ciar uma rota para a sua view de erro 404:
Route::get('/404', function () { return view('404'); });

//=======================
// VA EM: appExceptionsHandler.php = adiciono o IF na function render para redireciona para sua rota de p谩gina n茫o encontrada.

public function render($request, Exception $exception){

//====IF===
if ($exception instanceof
SymfonyComponentHttpKernelExceptionNotFoundHttpException)
{
return redirect(url('/404'));
}
//=======

return parent::render($request, $exception);

}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

klimentLambevski picture klimentLambevski  路  3Comments

lzp819739483 picture lzp819739483  路  3Comments

shopblocks picture shopblocks  路  3Comments

kerbylav picture kerbylav  路  3Comments

jackmu95 picture jackmu95  路  3Comments