Redirecting to the previous page on login in Laravel

0

Redirecting the user to the page they were previously browsing after logging in could be useful, especially when the users wanted to interact with the resources available on that page that is possible only to authenticated users. This feature is quite common amongst applications and there are many different ways a developer could go about implementing such a feature into their authentication system.

Such a feature could also be useful when logging the user out of the system but keeping them on the page that they were browsing rather than just redirecting them to a homepage (if the page is protected by an auth middleware, then they would be redirected to either home or to log-in page. Whatever is a default for your application to handle guests on guarded routes).

In this example I’ll be using Laravel’s breeze starter kit and it’s authentication system to implement this redirecting feature. We’ll be creating a middleware which would store the URL we would be redirecting to upon logging in or out.

The Middleware

To start, we will be creating a middleware to generate and store the URL we would be redirecting to upon logging in or out. We could create our middleware with the help of Artisan commands in the console. In this example, I’ll be calling the middleware ‘HandleRedirect

php artisan make:middleware HandleRedirect

That would generate us a core middleware class which we will fill with the logic for our URL generation and storage. The middleware would be stored in app/Http/Middleware/HandleRedirect.php.

We would be using three classes in our middleware which we would use to generate the URL, to moderate the generation and to store the URL.

	use Illuminate\Routing\UrlGenerator;
	use Illuminate\Support\Facades\Route;
	use Illuminate\Support\Facades\Session;

After importing those classes to the top of our middleware class, I suggest we use a constructor to set the base for our middleware handling logic.

    protected $url;

    public function __construct(UrlGenerator $url)
    {
        $this->url = $url;
    }

Once we got the constructor set up and it’s initiating the URL we are going to be using to generate the URL in our handling logic.

    public function handle(Request $request, Closure $next): Response
    {
         Session::put('previousUrl', $this->url->current());

        return $next($request);
    }

Now, while that would “mostly” work, we need to tweak that a bit to make sure it doesn’t effect the site in unexpected ways. First, we need to make sure it doesn’t work for routes that send a json response since we shouldn’t physically redirect to those routes. To do that, we have to add the following statement to the top of the handle method.

        if ($request->expectsJson()) {
            return $next($request);
        }

There are also some other routes that we want to exclude from storing a previous url session like the login route, the logout route and any unnamed routes since most/all of the POST request routes are unnamed. Redirecting users there wouldn’t be something that we want to do.

First, we set an array of routes we are going to ignore so this middleware doesn’t set the previousUrl session and user’s won’t redirect there.

    protected array $exceptRoutes = [
            'login', 'logout'
        ];

Than within the handle method we add the following bit of code that utilizes the $exceptRoutes and also checks if the route has a name. Only named routes that are not listed in $exceptRoutes array will utilize this middleware.

        if (!in_array(Route::currentRouteName(), $exceptRoutes) && Route::currentRouteName() !== null)
        {
            Session::put('previousUrl', $this->url->current());
        }

This way, we ensure that the middleware doesn’t cause any unwanted behavior in our application and still does it’s job.

The HandleRedirect middleware in it’s entirety is:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;
use Symfony\Component\HttpFoundation\Response;

class HandleRedirect
{
    protected $url;

    protected array $exceptRoutes = [
            'login', 'logout'
        ];

    public function __construct(UrlGenerator $url)
    {
        $this->url = $url;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->expectsJson()) {
            return $next($request);
        }

        if (!in_array(Route::currentRouteName(), $this->exceptRoutes) && Route::currentRouteName() !== null) {
            Session::put('previousUrl', $this->url->current());
        }

        return $next($request);
    }
}

Registering the Middleware

After creating our middleware, we need to utilize it in such a way that we could use it to set the previous url that we will use to redirect guests to their previously viewed page upon a successful log in, and redirecting authenticated users back to their respective pages upon logging out.

I feel it would be best if we add this middleware to the web middleware group so we won’t have to register every route to this middleware. This way, every route that is used in the web scope (not in API) would be using this middleware. If you want API routes to use this middleware as well, than add it to middleware array rather than middleware group array.

We need to register our middleware into the kernel which is found in app/Http/kernel.php which holds three protected arrays. I suggest we register it into the $middlewareGroups, specifically into the 'web' group. Just add our middleware to the end of that list:

	\App\Http\Middleware\HandleRedirect::class,

Once that is done, we are ready to use our middleware in the authentication logic of our application.

Implementing the Middleware

The Breeze starter kit for Laravel uses AuthenticatedSessionController which is found in app/Http/Controllers/Auth/AuthenticatedSessionController.php. In it, we would be looking at two methods; the store method and the destroy method.

Before we start messing with the methods, we need to use Session facade in the controller to be able to utilize the session variable we’ve set in the middleware.

	use Illuminate\Support\Facades\Session;

After using the Session facade, we are ready to modify the store method. To keep it simple, we would only mess with the line that handles the redirection.

    public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

        $request->session()->regenerate();
        
        return redirect()->intended(Session::get('previousUrl') ?? RouteServiceProvider::HOME);
    }

We are checking if the previousUrl is set first, and if it isn’t, we are using the default RouteServiceProvider::HOME constant.

The destroy method, the logout part of the authentication, is mostly the same, but since it clears all session variables upon logout, we need to extract the URL first thing in the method before we’re able to use it in the redirect function.

    public function destroy(Request $request): RedirectResponse
    {
        $destination = Session::get('previousUrl') ?? RouteServiceProvider::HOME;

        Auth::guard('web')->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();
        
        return redirect($destination);
    }

This ensures that we get our previousUrl from the session if it’s available before we clear all session variables upon logging out and the user should be redirected back to the page they are browsing upon clicking the logout button.

How do you handle such a feature to redirect users to the previously viewed pages?

Comments

No Comments. Be the first to make a comment.

Need to Register or Login to comment on post