Sharing Authentication cookies across Laravel Apps

bedram-tamang

Bedram Tamang

Posted on September 19, 2024

Sharing Authentication cookies across Laravel Apps

We have been working on a Laravel application for more than 7, 8 years, with increasing numbers of features, the application has become very large with more than 500k lines of PHP code, which has around 300k lines of Vue code as well. With increasing number of complexity and difficulties in upgrading the project, we decided to write all new APIs on the new Laravel (11x) project. Our main application already serving the vue frontend with Laravel's old style, where you define routes in Laravel and inject Vue components in blade templates. So our challenge was to authenticate our new api project with the same login credentials.

There are various ways to architect the communication between frontend to the backend, Out of many, API gateway and Direct client to microservice communication are two of them. API gateway provides a single entry point for a group of microservices, and enables us to write common features such as authentication, rate-limiting by acting as reverse proxy. In the direct client-to microservice communication approach, each microservice exposes an public endpoint, allows clients to connect with microservices directly. In this approach, each microservice is responsible for their authentication, rate-limiting, and all other services on their own.

Image description

Cookie Sharing

Consider you are building an e-commerce platform that has multiple microservices providing APIs for the front-end, and each of these microservices is independent of each others and offers direct client-to-service communications. There could be a service to register and login a user, let's say user service, so whenever a user logs in with this service this service will return some credentials (token or cookie), and that credential should be valid across all other services.

Direct Client to Microservice Communication

Important Consideration

There are some important things to consider before implementing cookie-sharing

  1. Cookie can be shared only in subdomains, for example, if the core application is running on jobins.jp, the cookie can be shared across the subdomains, let's say api.jobins.jp, but can't shared in jobins.com
  2. The session driver shouldn't be file. Here we are using the same database so both the application has access to same users table.
  3. The cookie name should be the same across all the services.
  4. The APP_KEY should be the same across all the services

Setup

To demonstrate the example of cookie sharing, Let's start by creating two different Laravel (api & core) applications inside a folder say 'cookie-sharing'.



mkdir cookie_sharing
cd cookie_sharing
laravel new core
laravel new api


Enter fullscreen mode Exit fullscreen mode

Further, I used laravel valet to configure the api and core sites to served from .test domain in my local machine. So, api project can be accessed from api.laravel.test and core project can be accessed from laravel.test.

I have installed Laravel breeze in laravel.test project to quicky setup registration and login feature.

Share Encryption Key

The first thing we require here is make the encryption key APP_KEY same in both of our project.

Sanctum Setup

We are using Laravel's Sanctum to authenticate our frontend served from our core application. So Sanctum can be easily installed on API project as:



php artisan install:api


Enter fullscreen mode Exit fullscreen mode

To authenticate the SPA, we need to further our sanctum middleware, cors and Cookie, at first the middleware can be configured as:



->withMiddleware(function (Middleware $middleware) {
    $middleware->statefulApi();
})


Enter fullscreen mode Exit fullscreen mode

and then the cors config can be published as



php artisan config:publish cors


Enter fullscreen mode Exit fullscreen mode

Cookie Domain

As you can see in the application tab of browser, the cookie name is laravel_session and domain is api.laravel.test for api site. We want this cookie to shared with the laravel.test site as well. So to share the cookie, we change the domain in config/sessions.php or simply change SESSION_DOMAIN in .env file to .laravel.test

Image description

Cookie Conflict

Since we are sharing same cookie into two Laravel sites, and Laravel sends the cookie headers in it's each response, and the browsers picks up the cookie that comes first into the response headers, Our main idea here would be to use cookie from the laravel.test site, as the cookie from api.laravel.test could be invalid and logout the users from application

Image description



class StartSession extends \Illuminate\Session\Middleware\StartSession
{
    protected function addCookieToResponse(Response $response, Session $session):void
    {
        parent::addCookieToResponse($response, $session);

        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig()){
            $response->headers->setCookie(new Cookie(
                $config['api']['session_cookie'],
                $session->getId(),
                $this->getCookieExpirationDate(),
                $config['path'],
                $config['api']['session_domain'],
                $config['secure'] ?? false,
                $config['http_only'] ?? true,
                false, $config['same_site'] ?? null
            ));
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Image description

Using Different Cookie in api.laravel.test

In the API site, we'll use the api_laravel_session cookie from the API routes. By default, Laravel uses the cookie name defined in the config/sessions.php file. To ensure the correct cookie is used for authentication, we'll swap the api_laravel_session cookie with laravel_session, allowing Laravel to retrieve the correct session value. Which we can retrieve using the middleware below.



class UseWebCookie
{
    public function handle(Request $request, Closure $next)
    {
        $cookie = $request->cookies->get(config('session.cookie_web'));
        $request->cookies->set(config('session.cookie'), $cookie);

        return $next($request);
    }
}


Enter fullscreen mode Exit fullscreen mode

And also, we are not returning the cookie in respond headers unless it's explicitly asked by users in request.



$response->headers->removeCookie(config('session.cookie'));

Enter fullscreen mode Exit fullscreen mode




Conclusion:

In conclusion, sharing authentication cookies across multiple Laravel applications can be effectively achieved by ensuring the apps share a common encryption key, domain, and session configuration. By using Laravel Sanctum for SPA authentication and properly managing cookie conflicts with separate session cookies for web and API routes, you can maintain consistent user sessions across different services. This approach provides a scalable solution for managing authentication in a microservices architecture while preventing potential cookie overwrites and session issues.

References:

💖 💪 🙅 🚩
bedram-tamang
Bedram Tamang

Posted on September 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related