Laravel Sanctum vs Passport: Choosing the Right API Authentication
Not every app needs Passport. Learn the key differences between Sanctum and Passport, when to use each, and how to implement secure token-based authentication.
SenpaiDev
Author
API authentication in Laravel can be confusing — you have Sanctum, Passport, and even plain session auth. Choosing the wrong one creates security headaches and unnecessary complexity. Here's how to make the right choice.
What Is Laravel Sanctum?
Sanctum is a lightweight authentication package for SPAs (Single Page Applications), mobile apps, and simple token-based APIs. It issues plain-text API tokens stored in your database and optionally uses Laravel's session cookies for first-party SPAs. No OAuth server overhead, no token encryption — just clean, fast token authentication.
Because Sanctum piggybacks on Laravel's existing session infrastructure for SPA use, your existing middleware, CSRF protection, and session configuration all continue to work. This makes it the right choice for 90% of Laravel projects.
What Is Laravel Passport?
Passport is a full OAuth2 server implementation. It supports authorization codes, client credentials, password grants, personal access tokens, and refresh tokens. It's the right tool when you need to let third-party applications authenticate on behalf of your users — think "Sign in with Google" but for your own platform.
Passport is significantly more complex: it generates encryption keys, maintains its own token storage tables, and requires understanding OAuth2 flows. If you don't need third-party delegation, you're trading simplicity for no benefit.
The Decision Framework
Ask yourself one question: Will external developers build apps that authenticate as your users?
- No (internal SPA, mobile app you control, simple API): Use Sanctum
- Yes (public API platform, marketplace, developer ecosystem): Use Passport
Implementing Sanctum for a Mobile App
Install and configure Sanctum:
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Issue tokens in your login controller:
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (!Auth::attempt($credentials)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$token = $request->user()->createToken(
name: $request->input('device_name', 'api-token'),
abilities: ['read', 'write'],
);
return response()->json(['token' => $token->plainTextToken]);
}
Protect your routes with auth:sanctum middleware and check abilities with $request->user()->tokenCan('write').
Token Abilities for Fine-Grained Authorization
Token abilities are Sanctum's killer feature. When creating a token, you define what it can do. This lets you issue read-only tokens for dashboard integrations and full-access tokens for your mobile app — all without separate authentication systems.
// Read-only token for integrations
$user->createToken('dashboard', ['read:reports', 'read:analytics']);
// Full-access token for the main app
$user->createToken('main-app', ['*']);
Security Best Practices
Always set token expiration by configuring sanctum.expiration in your config. Implement token revocation on logout. Use HTTPS exclusively. For SPAs, rely on cookie-based sessions rather than tokens — it's more secure because tokens aren't accessible to JavaScript.
Sanctum's simplicity isn't a limitation — it's a feature. Start with Sanctum, and only reach for Passport when a concrete business requirement demands OAuth2 delegation.
Laravel field notes
How To Apply This In A Real Laravel App
Use the article as a starting point, then validate the idea against the shape of your application. In Laravel projects, the safest pattern is to make the first version small, measurable, and easy to remove if the tradeoff is wrong.
Implementation approach
Start with one route, one controller or action, and one test that proves the expected behavior. Once the path is stable, extract shared code into a service class or action only if a second caller needs it.
For production work, keep config in environment variables, cache expensive reads, and add clear failure states. A feature that works locally but fails silently in a queue, scheduler, or cached config environment is not ready for users.
Review Checklist
- Add a feature or regression test before changing shared behavior.
- Run the route through production-like cache settings with config and route caching enabled.
- Check authorization, validation, and error responses before exposing the feature publicly.
- Document any non-obvious tradeoff in the code or article notes so future edits stay honest.
Written by
SenpaiDev
Developer and publisher at SenpaiDev, writing practical notes on Laravel, PHP, browser tools, and shipping better web products.
Comments (0)
Join the conversation
Log in to commentNo comments yet. Be the first to share your thoughts!