Centralized Authentication – Same Domain (e.g., subdomains)
Centralized Authentication – Different Domains
When to Use What?
Use Laravel Passport or Sanctum with Session Cookies
Centralized Authentication is a system where a single backend service (identity provider) manages user login, logout, and session state for multiple applications (clients)
Real-World Use Cases
Login to Gmail also gives access to YouTube and Drive (SSO via Google)
Corporate systems where login to the portal authenticates HR, Finance, and Email apps
Micro-frontend apps sharing the same user session
Types of Centralized Authentication
We’ll now categorize the approaches by:
Domain type (same vs different)
Technology (cookies, tokens, OAuth2, etc.)
Centralized Authentication – Same Domain (e.g., subdomains)
Centralized Authentication – Different Domains
When to Use What?
Use Laravel Passport or Sanctum with Session Cookies
How it works:
All your React apps are hosted under sub-paths or subdomains (e.g. api.example.com/users, api.example.com/orders or users.example.com, orders.example.com).
Laravel issues an HTTP-only cookie after login.
The browser sends the cookie automatically with every request — no need to pass tokens manually
Goal
User logs in from React App A
Automatically authenticated in React App B
All frontend apps share same Laravel backend and authentication
Use Laravel Sanctum and session-based cookies
Step 1: Install Sanctum in Laravel
In your Laravel backend:
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Step 2: Configure Sanctum
🔹 In config/sanctum.php:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost:3000,localhost:3001')),
This lets Laravel know which frontend origins are allowed to use cookies for auth.
In .env file:
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:3000,localhost:3001
Step 3: Configure CORS
Edit config/cors.php to enable cookie sharing and cross-origin requests:
return [
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout', 'user'],
'allowed_methods' => ['*'],
'allowed_origins' => ['http://localhost:3000', 'http://localhost:3001'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
];
Step 4: Middleware Setup
In app/Http/Kernel.php, add this:
// Ensure session and cookie middleware is applied
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
This lets Sanctum check for cookies in "stateful" frontend requests.
Step 5: Create Auth Routes
Example in routes/api.php:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::post('/login', function (Request $request) {
$credentials = $request->only('email', 'password');
if (!Auth::attempt($credentials)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$request->session()->regenerate();
return response()->json(['message' => 'Logged in successfully']);
});
Route::post('/logout', function (Request $request) {
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json(['message' => 'Logged out']);
});
🧑💻 Frontend (React) Setup
Do this in each React app (e.g. localhost:3000, localhost:3001):
Step 1: Install Axios
npm install axios
Step 2: Enable withCredentials Globally
// axiosSetup.js
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:8000'; // Laravel backend
axios.defaults.withCredentials = true;
export default axios;
Step 3: Login Flow in React
import axios from './axiosSetup';
const login = async () => {
try {
// Step 1: Get CSRF token
await axios.get('/sanctum/csrf-cookie');
// Step 2: Post login credentials
await axios.post('/login', {
email: 'test@example.com',
password: 'password123'
});
// Step 3: Fetch authenticated user
const user = await axios.get('/api/user');
console.log('Logged in as:', user.data);
} catch (err) {
console.error('Login failed:', err.response.data);
}
};
Step 4: Get Authenticated User in Other React Apps
const getUser = async () => {
try {
const user = await axios.get('/api/user');
console.log('Authenticated as:', user.data);
} catch (err) {
console.error('Not authenticated');
}
};
Centralized Authentication – Different Domains
composer require laravel/passport
php artisan migrate
php artisan passport:install
In App\Models\User.php
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
In AuthServiceProvider.php
use Laravel\Passport\Passport;
public function boot()
{
$this->registerPolicies();
Passport::routes(); // registers /oauth/authorize, /oauth/token
}
In config/auth.php
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Create OAuth2 Clients
php artisan passport:client
App 1 → redirect: https://app1.example.com/auth/callback
App 2 → redirect: https://app2.example.com/auth/callback
Save:
CLIENT_ID
CLIENT_SECRET
Login Controller (PassportLoginController.php)
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class PassportLoginController extends Controller
{
public function showLoginForm()
{
return view('auth.login');
}
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended();
}
return back()->withErrors([
'email' => 'Invalid credentials',
]);
}
}
Routes (routes/web.php)
use App\Http\Controllers\PassportLoginController;
Route::get('/login', [PassportLoginController::class, 'showLoginForm'])->name('login');
Route::post('/login', [PassportLoginController::class, 'login']);
Login View (resources/views/auth/login.blade.php)
<form method="POST" action="/login">
@csrf
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
🧑💻 React App 1 & 2 Code (Same Structure)
Axios Client (axiosClient.js)
import axios from 'axios';
const axiosClient = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Content-Type': 'application/json'
}
});
export default axiosClient;
Login.js (React)
const CLIENT_ID = 'YOUR_CLIENT_ID';
const REDIRECT_URI = 'https://app1.example.com/auth/callback'; // change for app2
export const login = () => {
window.location.href = `https://api.example.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=`;
};
AuthCallback.js
import axiosClient from './axiosClient';
import { useEffect } from 'react';
const AuthCallback = () => {
useEffect(() => {
const code = new URLSearchParams(window.location.search).get('code');
if (code) {
axiosClient.post('/oauth/token', {
grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: 'https://app1.example.com/auth/callback',
code: code
}).then(response => {
const { access_token } = response.data;
localStorage.setItem('access_token', access_token);
axiosClient.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
window.location.href = '/dashboard'; // or wherever you want
});
}
}, []);
return <div>Logging you in...</div>;
};
export default AuthCallback;
Authenticated API Request (Anywhere)
const token = localStorage.getItem('access_token');
axiosClient.get('/api/user', {
headers: {
Authorization: `Bearer ${token}`
}
}).then(res => {
console.log('User:', res.data);
});
🧪 Bonus: React Router Setup
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/auth/callback" element={<AuthCallback />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</BrowserRouter>
Top comments (0)