Search code examples
phplaravelauthenticationlaravel-5.4legacy

Force Laravel to log in a user using legacy authentication


I am trying to slowly integrate Laravel into a legacy PHP application. One of the tasks is to automatically register a Laravel user session when a user logs in to the old app. I am not trying to implement Laravel authentication, I really just want to piggyback off of existing functionality and force a specific user to be logged in without checking credentials. What I have so far has been cobbled together from other people's hacks I have found around:

// Laravel authentication hook - Boostrap application
require_once getcwd() . '/../laravel/bootstrap/autoload.php';
$app = require_once getcwd() . '/../laravel/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$kernel->bootstrap();
$app->boot(); 

// Start Laravel session
$request = Illuminate\Http\Request::capture();
$response = $app->make('Symfony\Component\HttpFoundation\Response');
$startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
// Associate server session with the authenticating user id
// I have also tried loading user model instance and then $app['auth']->login($user)
$app['auth']->loginUsingId($user_id);

$app['session']->driver()->start();
// Terminate middleware response chain with naked response
$response = $startSession->handle($request, function() use($response) {
    return $response; // This response will have session cookie attached to it
});

$response->send();

After this I get a laravel_session cookie that has contents on the client. During the login request after the code above executes, if I dd(Auth::user()) then I get the user I just logged in with. However, on subsequent requests Auth::user() and $this->request->user() both return null in all contexts.

How can I force an active Laravel user session without actually authenticating that will persist across requests?


The ultimate outcome is that Laravel will be running as a 'sub-application' underneath the legacy app while existing features are pulled in one by one so that both will exist for a period of time until all features are implemented in Laravel and it will replace the existing app in full. If it makes more sense to try to take over the legacy authentication with Laravel rather than the other way around I'm open to that, but I'd rather avoid having to change the underlying users table (legacy authentication is happening over LDAP, so there are no passwords locally, there's no remember_token, but that's easy enough to add if I have to). I really am just looking for the shortest path with the least amount of effort/headache.


Solution

  • This is a little bit tricky because Laravel use encrypted cookies which is handled by EncryptCookies middleware. You can get your code working if you disable it but I wouldn't recommend it.

    The solution is to use Laravel EncryptCookies middleware to decrypt the request and then encrypt the response. This will make the session created by your legacy authentication readable by Laravel.

    Consider this is called login.php file, which needs $user_id to log the user by id to laravel.

    <?php
    
    require_once getcwd() . '/../laravel/bootstrap/autoload.php';
    $app = require_once getcwd() . '/../laravel/bootstrap/app.php';
    $kernel = $app->make('Illuminate\Contracts\Console\Kernel');
    $kernel->bootstrap();
    $app->boot();
    
    $request = Illuminate\Http\Request::capture();
    $response = $app->make('Symfony\Component\HttpFoundation\Response');
    $startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
    $encryptCookies = new App\Http\Middleware\EncryptCookies($app['encrypter']);
    
    $app['session']->driver()->start();
    
    $app['auth']->loginUsingId($user_id);
    
    $response = $encryptCookies->handle($request, function ($request) use ($startSession, $response)
    {
        return $startSession->handle($request, function () use ($response)
        {
            return $response;
        });
    });
    
    $app['session']->driver()->save();
    
    var_dump($app['auth']->user());
    
    $response->send();
    

    Inside the closure of $encryptCookies->handle(), you can read the request after decryption and this is where you can modify the session. And when you return the response, it will be encrypted again and you can then send it to the browser.

    To read the session in another file you can simply do this:

    <?php
    
    require_once getcwd() . '/../laravel/bootstrap/autoload.php';
    $app = require_once getcwd() . '/../laravel/bootstrap/app.php';
    $kernel = $app->make('Illuminate\Contracts\Console\Kernel');
    $kernel->bootstrap();
    $app->boot();
    
    $request = Illuminate\Http\Request::capture();
    $response = $app->make('Symfony\Component\HttpFoundation\Response');
    $startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
    $encryptCookies = new App\Http\Middleware\EncryptCookies($app['encrypter']);
    
    $response = $encryptCookies->handle($request, function ($request) use ($startSession, $response)
    {
        return $startSession->handle($request, function () use ($response)
        {
            return $response;
        });
    });
    
    var_dump($app['auth']->user());
    
    $response->send();