DEV Community
Laravel Sanctum vs Passport vs JWT: Which Auth to Use
"Sanctum, Passport or JWT?" gets asked as if the three were competitors. They aren't quite. Sanctum and Passport are first-party Laravel packages. JWT is not a package at all: it's a token format (RFC 7519) that third-party Laravel packages build on. The useful question is not "which is best" but who is calling your API, and do you need OAuth2? For most Laravel applications the answer is Sanctum, and Laravel's own documentation says so. This article explains why, shows what each option does with real output, and covers the cases where Passport or JWT is the right call. I ran Sanctum 4.4.0 in a Laravel 13.40 app and tested JWT behavior with firebase/php-jwt 7.2.1. Passport I could only check against the documentation, and I say so where it matters. The short answer Sanctum Passport JWT (community packages) What it is Lightweight first-party package: cookie auth for SPAs, plus simple API tokens First-party full OAuth2 server A signed token format; packages such as tymon/jwt-auth and php-open-source-saver/jwt-auth wrap it Best for Your own SPA, mobile app, or simple API tokens Third parties logging in through your app (OAuth2), MCP servers Stateless verification across services, if you accept the trade-offs Revoking a token Delete a row; immediate Revoke server-side Needs server-side state (a denylist) to revoke early Setup weight Light Heavy (clients, keys, grants) Medium, plus your own key and lifetime decisions The decision flow, in order: Is it a regular browser app on the same Laravel project? Use Laravel's built-in session authentication; you don't need any of these. Is it your own SPA or mobile app, or do you just want to hand out API tokens? Sanctum. Do outside developers need "Log in with your app" or standard OAuth2 flows, or will an MCP client connect? Passport. Do several independent services need to verify tokens without calling back to one database? Consider JWT, and plan revocation first. Sanctum: two things in one package The docs describe two separate problems Sanctum solves. First, API tokens: a simple way to issue tokens "without the complication of OAuth", stored in one database table and sent in the Authorization header. Second, SPA authentication, where Sanctum doesn't use tokens at all and relies on Laravel's cookie-based session authentication instead, only for requests that come from your own SPA frontend. It is fine to use only one of the two. The setup from the docs is php artisan install:api, then the HasApiTokens trait on your user model: // app/Models/User.php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Protect routes with the sanctum guard. The abilities check needs its middleware alias registered once in bootstrap/app.php: // bootstrap/app.php, inside withMiddleware()
$middleware->alias([
'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,
]);
// routes/api.php
Route::middleware('auth:sanctum')->get('/user', fn (Request $request) => $request->user()->only('id', 'name', 'email'));
Route::middleware(['auth:sanctum', 'abilities:orders:read'])->get('/orders', fn () => ['orders' => []]);
Route::middleware(['auth:sanctum', 'abilities:orders:write'])->post('/orders', fn () => ['created' => true]);
Route::middleware('auth:sanctum')->post('/logout', function (Request $request) {
$request->user()->currentAccessToken()->delete();
return ['ok' => true];
});
Issuing tokens is one call. This is the pattern from the docs for a mobile login endpoint, which checks the credentials and returns a token named after the device: Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request...
Powered by Readybot.io
External Link
Platform
You are about to visit:
https://example.comThis will open in a new tab. Make sure you trust this link.