Sharing Authentication cookies across Laravel Apps
Bedram Tamang
Posted on September 19, 2024
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.
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.
Important Consideration
There are some important things to consider before implementing cookie-sharing
- 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 sayapi.jobins.jp
, but can't shared injobins.com
- The session driver shouldn't be file. Here we are using the same database so both the application has access to same users table.
- The cookie name should be the same across all the services.
- 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
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
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();
})
and then the cors config can be published as
php artisan config:publish cors
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
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
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
));
}
}
}
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);
}
}
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'));
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:
- https://learn.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern
- https://microservices.io/patterns/apigateway.html
- https://microservices.io/patterns/data/api-composition.html
- https://laravel.com/docs/11.x/sanctum
Posted on September 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.