You have a Vue or React app that talks to Laravel. Or a mobile app consuming your APIs. The first wall you hit? Authentication. It's not just about sending a password — you need to protect every endpoint, handle tokens, refreshes, expirations, and make sure both a SPA frontend and a native app can connect without headaches.
Laravel provides two official solutions: Sanctum and Passport. At Meteora Web, we work with Laravel every day — we chose it for our stack precisely because of the total control it gives without lifetime vendor lock. In this guide we'll break down the differences and, most importantly, when to use each.
What is Laravel Sanctum and how does it work?
Sanctum was built to simplify authentication for SPAs (Single Page Applications) and API token-based mobile or third-party apps. It is not a full OAuth2 implementation — it's a lightweight wrapper around Laravel's native API tokens.
How it works for SPA?
Sanctum leverages Laravel's session cookies. The user logs in, the server sets a session cookie (with SameSite=Strict), and all subsequent requests are authenticated via the session, not a token. This is more resistant to XSS attacks because the token is never exposed to JavaScript.
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000')),On the frontend (Vue/Axios):
axios.get('/api/user', { withCredentials: true });How it works for API tokens?
For mobile apps or external services, Sanctum generates plain Bearer tokens. The user requests a token via login, stores it client-side, and sends it in the Authorization: Bearer ... header. Simple, no OAuth complexity.
Sponsored Protocol
// App\Models\User
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
}// Creating a token
$token = $user->createToken('mobile-app', ['read', 'write'])->plainTextToken;Sanctum is perfect when you control both the frontend and backend and don't need the OAuth2 protocol (e.g. untrusted third-party clients).
What is Laravel Passport and why was it created?
Passport is a full OAuth2 Server implementation. It was introduced when Laravel needed to serve public APIs to external clients (e.g., developers integrating your platform). OAuth2 allows granting limited access to resources without sharing the user's password.
Passport supports all grant types: Authorization Code, Client Credentials, Password Grant, Personal Access Tokens, Refresh Tokens. It's more complex to set up but is the standard for public APIs.
Example with Password Grant
// routes/api.php
Route::post('/oauth/token', 'Laravel\Passport\Http\Controllers\AccessTokenController@issueToken');
// Typical request:
// POST /oauth/token
// grant_type=password
// client_id=...
// client_secret=...
// username=...
// password=...
// scope=...Passport requires extra database tables (oauth_clients, oauth_access_tokens, etc.) and running its migrations.
Sponsored Protocol
What are the main differences between Sanctum and Passport?
The question we hear most often: “Which one should I use?”. The answer depends on who consumes your APIs.
- Sanctum: built for frontends you control (SPA + your own mobile app). Zero OAuth overhead, simple tokens, cookie-based auth for SPAs.
- Passport: built for public APIs consumed by third-party clients (e.g., another company's mobile app, an external service). Provides full OAuth2 with refresh tokens and scopes.
From a maintenance perspective, Sanctum is much leaner — no extra migrations, no oauth tables. We prefer it for 80% of projects. We only bring out Passport when the OAuth2 protocol is actually required.
How to implement Sanctum authentication for SPA?
Assume you have a Vue app at app.example.com and Laravel at api.example.com. We want login via session cookie.
Step 1: Configure stateful domains
// .env
SESSION_DOMAIN=.example.com
SANCTUM_STATEFUL_DOMAINS=app.example.comStep 2: API middleware
In the api middleware, make sure SPA routes use web middleware (for sessions) and Sanctum:
// routes/api.php
Route::middleware(['api', 'auth:sanctum'])->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
});Step 3: Login (backend)
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (Auth::attempt($credentials, $request->boolean('remember'))) {
$request->session()->regenerate();
return response()->json(['user' => Auth::user()]);
}
return response()->json(['message' => 'Invalid credentials'], 401);
}Step 4: Frontend (Axios)
// Set withCredentials globally
axios.defaults.withCredentials = true;
// Login
axios.post('https://api.example.com/login', {
email: 'user@example.com',
password: '...'
}).then(response => {
// Now the user has a session cookie
// Call /api/user without any token
});
// Logout
axios.post('https://api.example.com/logout');Important: CORS must allow credentials. Configure cors.php with supports_credentials => true and proper allowed_origins.
Sponsored Protocol
How to implement Passport authentication for mobile API?
Now a typical use case: a third-party React Native app wants to authenticate with our APIs using Password Grant (trusted client).
Step 1: Install Passport
composer require laravel/passport
php artisan passport:installThis generates keys and creates a client with the password grant.
Step 2: Configure User model
// App\Models\User
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
}Step 3: Token routes
Passport already exposes token routes. Just add:
Sponsored Protocol
// routes/api.php
Route::post('/oauth/token', 'Laravel\Passport\Http\Controllers\AccessTokenController@issueToken');
// Protect routes with auth:api
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});Step 4: Mobile side (example Flutter with http package)
http.Response response = await http.post(
Uri.parse('https://api.example.com/oauth/token'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'grant_type': 'password',
'client_id': 'your-client-id',
'client_secret': 'your-secret',
'username': 'user@example.com',
'password': '...',
'scope': '',
}),
);
var data = jsonDecode(response.body);
String accessToken = data['access_token'];
// Save token in secure storageThen use the token in every request: Authorization: Bearer $accessToken.
Passport handles refresh tokens automatically: when you get a 401, request a new token at /oauth/token with grant_type=refresh_token.
Which one should you choose for your project and why?
The choice is straightforward if you ask yourself these questions:
- Do you control both frontend and backend? Use Sanctum. Faster to set up, no OAuth complexity, and the cookie-based security for SPAs is superior.
- Do you need to expose APIs to external clients? Use Passport. OAuth2 is the standard — don’t reinvent the wheel with custom tokens.
- Do you already have a mobile app that consumes other OAuth2 APIs? Passport integrates better with external providers (e.g., Google, Facebook).
- Still unsure? Start with Sanctum. You can migrate to Passport later if real OAuth2 needs arise. We did this in a B2B e‑commerce project: Sanctum for the internal SPA admin panel, then Passport for the public partner API.
At Meteora Web we use Sanctum for internal dashboards and Passport for public APIs in our proprietary stack. We know the costs of both — not only in code, but in maintenance and security. If you want to dive deeper into the complete architecture of a Laravel enterprise application, check out our Laravel Framework Pillar.
Sponsored Protocol
What to do next
- Identify whether your APIs will be consumed by frontends you control or by third parties.
- For Sanctum: run
composer require laravel/sanctumand follow the steps above. - For Passport: run
composer require laravel/passportthenphp artisan passport:install. - Immediately protect sensitive routes — never leave APIs public without authentication.
- Read the official documentation: Sanctum docs and Passport docs for advanced cases (scopes, refresh tokens, multi-token).
API security is something we see consistently underestimated in Italian SMEs. Don’t fall into the trap of rolling your own tokens. Sanctum and Passport are maintained by the Laravel team, well tested, and secure. Pick the right one and build your API knowing the hardest part is already solved.