Laravel Sanctum vs Passport: which API authentication package to use
Sanctum and Passport both authenticate Laravel APIs, but they solve different problems. This comparison explains tokens, SPA cookies, OAuth2 grants, expiry and clean-up in Laravel 13, and when each package is the right choice.
8 min read

In this article
Almost every Laravel project that grows beyond a website needs API authentication sooner or later: a mobile app, a single-page front end, a partner integration or a command-line tool. Laravel offers two first-party packages for this, Sanctum and Passport, and teams regularly pick the heavier one when the lighter one would do, or the other way round.
This article explains what each package actually does in Laravel 13, where they overlap, and which one fits common situations. It is based on the official Laravel 13.x documentation for both packages, read on 7 October 2026. The short version from that documentation: if you truly need OAuth2, use Passport; for single-page applications, mobile apps and simple API tokens, use Sanctum.
The core difference in one paragraph
Laravel Sanctum is a lightweight authentication system. It issues personal API tokens that your own users create and send as bearer tokens, and it can authenticate your own single-page application using Laravel's normal session cookies. It does not implement OAuth2. Laravel Passport is a full OAuth2 server built on the League OAuth2 server library. It lets other applications obtain tokens on behalf of your users through standard OAuth2 grants, with clients, scopes, refresh tokens and consent screens.
So the deciding question is not "which is more secure" but "who are the clients?" If the clients are your own front end, your own mobile app or your users' scripts, Sanctum is usually enough. If third-party developers will build applications that ask your users for permission, you need an OAuth2 server, which is Passport.

Laravel Sanctum in Laravel 13
Installation
Sanctum is installed by the same command that enables API routing:
php artisan install:apiThis creates routes/api.php and installs Sanctum. Protect routes with the auth:sanctum middleware.
API tokens
Add the Laravel\Sanctum\HasApiTokens trait to your User model, then issue tokens with createToken(). Sanctum stores tokens hashed with SHA-256 in a single database table, so the plain-text value is available only once, at creation time:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/tokens', function (Request $request) {
$request->validate(['token_name' => ['required', 'string', 'max:100']]);
$token = $request->user()->createToken(
$request-$request->string('token_name')gt;input('token_name'),
['orders:read'],
now()->addDays(90),
);
return ['token' => $token->plainTextToken];
})->middleware('auth:sanctum');The second argument lists abilities, which play the same role as OAuth scopes. Check them with $user->tokenCan('orders:read'), or with the abilities and ability middleware after registering their aliases in bootstrap/app.php. The third argument sets an expiry for that token.
By default, Sanctum tokens never expire; they remain valid until revoked. You can set a global expiration in minutes in the Sanctum configuration file, or per token as shown above. If you use expiry, schedule the prune command so old rows do not pile up:
use Illuminate\Support\Facades\Schedule;
Schedule::command('sanctum:prune-expired --hours=24')->daily();Revoking is a database delete: $user->tokens()->delete() removes all of a user's tokens, and $request->user()->currentAccessToken()->delete() removes the one used for the current request.
SPA authentication
Sanctum's second feature authenticates your own single-page application without tokens at all. It uses Laravel's cookie-based session, which brings CSRF protection and keeps credentials out of JavaScript-readable storage. The requirements, per the documentation:
- The SPA and the API must share the same top-level domain (they may be on different subdomains).
- List the SPA's domains in Sanctum's
statefulconfiguration. - Call
$middleware->statefulApi()inbootstrap/app.php. - Configure CORS to allow credentials (
supports_credentialsset totrueafter publishingconfig/cors.php).
The Sanctum documentation is explicit that you should not use API tokens to authenticate your own first-party SPA; use the cookie-based SPA authentication instead.
Laravel Passport in Laravel 13
Installation
Passport also installs through install:api, with a flag:
php artisan install:api --passportThe command publishes and runs the migrations for OAuth2 clients and tokens and generates the encryption keys used to sign access tokens. You then add the Laravel\Passport\HasApiTokens trait and the Laravel\Passport\Contracts\OAuthenticatable interface to the User model and define an api guard with the passport driver in config/auth.php.
When deploying for the first time, run php artisan passport:keys. The generated keys are normally kept out of source control, so plan how they reach each server.
Grants
Passport's value lies in its OAuth2 grants. The Laravel 13 documentation covers:
| Grant | Typical use | Status in the docs |
|---|---|---|
| Authorization code | Third-party web apps acting for a user | Standard |
| Authorization code with PKCE | SPAs and mobile apps that cannot keep a client secret | Documented for SPAs and mobile apps |
| Device authorization | TVs, consoles and other devices with limited input | Supported |
| Client credentials | Machine-to-machine calls with no user | Supported |
| Personal access tokens | Users creating their own tokens | Supported |
| Password | First-party clients sending username and password | "We no longer recommend" |
| Implicit | Legacy browser clients | "We no longer recommend" |
If an older tutorial tells you to use Passport's password grant for your mobile app, note that the current documentation advises against it. For a first-party mobile app, Sanctum tokens or Passport's authorization code with PKCE are the documented paths.
Tokens and housekeeping
Passport issues access tokens that, by default, expire after one year; you can change lifetimes with Passport::tokensExpireIn(), refreshTokensExpireIn() and personalAccessTokensExpireIn() in AppServiceProvider. Revoked and expired tokens, authorization codes and device codes can be removed with php artisan passport:purge, which is a good candidate for the scheduler.

Side-by-side comparison
| Question | Sanctum | Passport |
|---|---|---|
| Implements OAuth2? | No | Yes, full server |
| Install command | php artisan install:api | php artisan install:api --passport |
| Token storage | SHA-256 hashed rows in one table | OAuth2 tables for clients, tokens, codes |
| Token permissions | Abilities | Scopes |
| Default token expiry | Never (configurable) | One year (configurable) |
| Refresh tokens | No | Yes |
| First-party SPA | Cookie session auth, built in | Authorization code with PKCE |
| Third-party apps with consent screen | Not supported | Supported |
| Signing keys to manage | None | Yes (passport:keys) |
| Cleanup command | sanctum:prune-expired | passport:purge |
Which one should you choose?
Choose Sanctum when
- You are building an SPA (for example with Next.js, Nuxt, React or Vue) on the same top-level domain as the API.
- You ship your own mobile app and want each device to hold its own revocable token.
- Your users need "personal access tokens" for scripts, similar to the tokens developer platforms let you generate in account settings.
- You want the smallest amount of moving parts to secure and maintain.
Choose Passport when
- Other companies or developers will register applications that act on behalf of your users, and you need consent screens, client IDs and scopes.
- You must interoperate with tools that expect a standard OAuth2 authorization server.
- You need machine-to-machine access via the client credentials grant, or the device authorization grant.
- You need refresh tokens and short-lived access tokens as part of an OAuth2 design.
When neither is the answer
If you want users to sign in with Google, GitHub or another provider, that is the job of Laravel Socialite, not Sanctum or Passport. And if your organisation already runs an identity provider for single sign-on, you may only need to validate its tokens rather than run your own OAuth2 server. Avoid adding Passport "just in case": every OAuth2 grant you enable is extra surface to secure.
Migration notes
Moving from Sanctum to Passport later is possible, but tokens are not shared between the two, so existing clients must obtain new tokens. Plan a transition period in which both guards are accepted on the relevant routes, communicate the change to API consumers, and remove the old guard once traffic has moved. Moving from Passport to Sanctum is usually done when a team realises no third-party clients ever materialised; the same dual-guard approach applies.
Security checklist for either package
- Set expiry on tokens and prune expired ones on a schedule.
- Grant the narrowest abilities or scopes a client needs, and check them in your policies, not only in middleware.
- Give users a screen to see and revoke their tokens.
- Rate-limit login and token-issuing routes.
- Serve the API only over HTTPS and never log bearer tokens.
- For Passport, keep signing keys out of the repository and restrict file permissions on the server.
A worked decision for a small SaaS team
Consider a typical small product: a Laravel back end, a React front end served from app.example.com, an API at api.example.com, and a request from a few customers to automate exports with scripts. Both front end and API share the top-level domain example.com, so Sanctum's cookie-based SPA authentication fits the web app, and Sanctum personal access tokens with an exports:read ability fit the customer scripts. No third party needs delegated access, so there is nothing for Passport to do.
Now suppose that a year later a partner wants to build an integration that its own customers connect to your product with a "Connect" button. That is delegated access with user consent, the classic OAuth2 case, and it is the point where adding Passport (or an external authorization server) becomes justified. Making the decision this way, from actual clients rather than anticipated ones, keeps the system as simple as it can be for as long as possible.
Summary
Sanctum and Passport solve different problems. Sanctum is the default for authenticating your own SPA, mobile app and user-generated API tokens; it is simpler to run and has fewer parts to get wrong. Passport is the right tool when you need a real OAuth2 server for third-party applications, machine clients or devices. Start by listing who will call your API, and the choice usually becomes obvious.
Frequently asked questions
Is Sanctum less secure than Passport?
Not inherently. They cover different needs. Sanctum hashes tokens with SHA-256 and uses Laravel's session cookies for first-party SPAs. Passport implements OAuth2 for third-party clients. Security depends mainly on configuration: expiry, narrow abilities or scopes, HTTPS and rate limits.
Do Sanctum tokens expire?
By default, no. Set an
expirationvalue in the Sanctum configuration or pass an expiry date tocreateToken(), and schedulesanctum:prune-expired.Should I use Passport's password grant for my mobile app?
The Laravel 13 Passport documentation says it no longer recommends the password grant. Use Sanctum tokens or Passport's authorization code grant with PKCE instead.
What should I use for 'Sign in with Google'?
Laravel Socialite, which handles authentication with OAuth providers such as Google, GitHub and others. Sanctum or Passport then secure your own API.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Laravel Sanctum (Laravel 13.x) — Laravel
- Laravel Passport (Laravel 13.x) — Laravel
- Routing: API Routes (Laravel 13.x) — Laravel
- Laravel Socialite (Laravel 13.x) — Laravel
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

Laravel
Upgrading from Laravel 12 to Laravel 13: a practical guide
Laravel 12 lost bug-fix support in August 2026. This guide covers the PHP 8.3 requirement, Composer changes, the CSRF middleware rename, cache and session hardening, low-impact changes that often bite, and a safe rollout plan.
By Cloudsoft Zone

TutorialLaravel
Laravel AI SDK 1.0 tutorial: build your first AI agent in Laravel 13
Build a tested Laravel 13 AI agent with laravel/ai 1.x: providers, a PHP tool, structured output, queued jobs, fakes for tests, cost caps and a security checklist.
By Cloudsoft Zone

TutorialLaravel
Connect Laravel AI agents to MCP servers: a practical guide
Expose a Laravel 13 app as an MCP server with laravel/mcp, then give Laravel AI SDK agents tools from MCP servers with tokens, OAuth and human approval for writes.
By Cloudsoft Zone
