Authenticate Legacy Project Users in Laravel

I really like working with Laravel. Whenever I need to extend some older project I want to do it in this awesome framework. Some times it is possible sometimes not. I wanted to start one feature in Laravel and gradually transfer all other parts of the old system too.

What to do when you want to share authenticated users?

I figured out two solutions:

  1. Make Laravel sub directory in existing project. Problem with this may be some dodgy apache config in the old project that would prevent that from working
  2. The second is to make a sub domain.

This way they could share sessions between themselves.

We need to tell old project to share cookies on all sub domains. Add this on the place you're starting sessions.

session_name('gather-rs');
// set top lvl domain
$domainParts = array_slice(explode('.', $_SERVER['HTTP_HOST']), -2, 2);
$domain = implode('.', $domainParts);
session_set_cookie_params(0, '/', '.'.$domain);
session_start();

Pay attention on session name, it has allowed set of characters to be used in the name. session_start(): The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'

Also critical thing is to always use same domain for cookies. If you have different sub domains, always use top level domain for cookies to avoid different kind of problems.

When changing these things make sure you clear your browser cookies.

From version 5 Laravel (read more here) is not using PHP sessions. That is actually a good thing. We will NOT change config/session.php config since we need to start native session and read from global $_SESSION variable, and these from session.php are only for Laravel session drivers. So Ignore it!

Only thing we need to change is session cookie domain. To do that, open .env file and add folowing. (You could do it in session.php - this is better).

SESSION_DOMAIN=.yourdomain.com

Now let's make a nice guard which will try to pull session data from old system and authenticate the user based on some criteria (usually user id).

Create a custom Laravel guard which will handle legacy user sessions.

<?php

namespace App;


use Illuminate\Auth\SessionGuard;

class LegacySessionGuard extends SessionGuard
{

    public function user()
    {


        if ($this->loggedOut) {
            return;
        }

        if (!is_null($this->user)) {
            return $this->user;
        }

        $id = array_get($this->getSessionData(), 'user_info.id');

        $user = null;

        if (!is_null($id)) {
            $user = $this->provider->retrieveById($id);
        }

        return $this->user = $user;
    }

    protected function getSessionData()
    {
        // Here we will start native PHP Session with
        // same name that we set in our old project
        if (session_status() == PHP_SESSION_NONE) {
            session_name('gather-rs');
            $domainParts = array_slice(explode('.', $this->getRequest()->getHost()), -2, 2);
            $domain = implode('.', $domainParts);
            session_set_cookie_params(0, '/', '.'.$domain);
            session_start();
        }
        return $_SESSION;
    }

}

Next we need to register (extend) it somehow. We can do that in the app/Providers/AuthServiceProvider.php.

Change boot method to this:

public function boot(GateContract $gate, AuthManager $auth)
{
    $this->registerPolicies($gate);

    $auth->extend('legacy', function ($app) use ($auth){
        $config = $app['config']['auth.guards.legacy'];
        $provider = $auth->createUserProvider($config['provider']);
        return new LegacySessionGuard('legacy', $provider, $this->app['session.store']);
    });

}

Here we are extending auth manager with new guard, but if you try to use it you would see something like Auth guard [legacy] is not defined. That is because you must define it in the config/auth.php (laravel something internally).

'guards' => [
    // ...
    'legacy' => [
        'driver' => 'legacy',
        'provider' => 'users'
    ]
],

Now you can use newly created guard. How you want to use it - it depends. You could immediately "transfer" legacy user session to the new app, or keep using old one. If you decide for a second one you can set default guard to be legacy in config/auth.php. I chose first option - to immediatelly transfer user to a new session handled by default web guard.

My Authenticate middleware looks like this:

public function handle($request, Closure $next, $guard = null)
 {
     // If user is logged on legacy system but not on
     // the new one
     $legacyGuard = Auth::guard('legacy');
     if (Auth::guard($guard)->guest() && $legacyGuard->check()){
         Auth::guard($guard)->login($legacyGuard->user());
     }

     if (Auth::guard($guard)->guest()) {
         if ($request->ajax() || $request->wantsJson()) {
             return response('Unauthorized.', 401);
         }

         return redirect()->guest('login');
     }

     return $next($request);
 }

These are more of a notes then a tutorial. Also I'm using same users table in both systems, old and new one, so that makes things easier I guess.

Author

I plan to write more articles about common laravel components. If you are interested let’s stay in touch.
comments powered by Disqus