Back to blog
Laravel
intermediate

Laravel Sanctum vs Passport: A Comprehensive Guide to API Authentication

Choosing the right authentication method can make or break an API's security and developer experience. This guide compares Laravel Sanctum and Passport, outlines step‑by‑step setup, and provides production‑ready code examples to help you decide which token system best fits your project.

December 19, 2025

Introduction

API authentication is the cornerstone of any modern web or mobile application that exchanges data across trusted boundaries. In the Laravel ecosystem, developers have two mature packages to choose from: Laravel Sanctum and Laravel Passport. Both aim to issue and manage tokens that protect routes, but they differ in complexity, use cases, and performance. This article will walk you through the concepts, architecture, and practical steps required to implement each system, compare their features, and highlight best practices for production deployments. Whether you are building a single-page application (SPA), a mobile app, or a decoupled microservice, understanding the nuances between Sanctum and Passport will empower you to make an informed decision that balances security, ease of use, and scalability. By the end of this guide, you will have a clear roadmap for setting up token authentication, writing protected API routes, handling token scopes, and troubleshooting common issues in a real-world Laravel project.

Sanctum, released in Laravel 8, was designed for simplicity and performance. It focuses on issuing API tokens that can be attached to either users or means‑that‑anyone (single‑token) and supports abilities like stateless authentication for SPAs and mobile clients. Passport, originally built before Sanctum, follows the OAuth 2.0 protocol and provides a full authorization server with client registration, token issuance, and refresh token handling. While Sanctum is ideal for many modern use cases, Passport remains valuable for legacy systems or when fine‑grained OAuth 2.0 flows are required. This article provides a side‑by‑side comparison, step‑by‑step tutorials, and production‑ready code snippets to help you pick the right tool for your project.

Table of Contents

Core Concepts

Before diving into implementation, it helps to understand the underlying protocols that Sanctum and Passport leverage. OAuth 2.0 is an industry‑standard framework for delegated access, defining four key components: authorization grant, access token, refresh token, and client credentials. Sanctum simplifies this model by providing a lightweight token system that still supports scopes, allowing you to restrict what a token can do without the full OAuth 2.0 handshake. Passport implements the full OAuth 2.0 flow, issuing tokens after a user authorizes a client, which is useful when you need to support third‑party applications accessing your API on behalf of users.

Tokens in Laravel can be either personal access tokens (used for internal calls) or incidence tokens (used for authenticated requests). Sanctum calls its tokens simply API tokens and can be attached to a user model or a “–” that allows token‑only access. Passport generates access tokens and refresh tokens, and it maintains a database table for clients, personal access clients, and revocations. Both packages store tokens in the database, allowing you to revoke them and inspect token usage, which is essential for security auditing.

Scopes define the permissions attached to a token. In Sanctum, scopes are optional strings that you can assign to an API token, for example “posts:read” or “posts:write”. Passport supports scopes as part of the OAuth 2.0 specification and can enforce them server‑side. By leveraging scopes, you prevent over‑privileged tokens and limit the blast radius if a token is compromised. Both packages also provide mechanisms to associate tokens with users, which enables you to build fine‑grained authorization checks inside your controllers.

Finally, think about token storage and presentation. Tokens are typically sent in the Authorization header using the Bearer scheme. Sanctum recommends using stateless middleware for SPAs, where the token never touches the server’s session store. Passport, on the other hand, can also work with stateful sessions, especially when using the token revocation table to blacklist tokens. Choosing the right storage strategy affects performance, security, and the developer experience of integrating the API with client applications.

Architecture Overview

At a high level, both Sanctum and Passport sit on top of Laravel’s middleware and routing system. When a request arrives, Laravel checks for an `Authorization` header, extracts the token, and passes it to the relevant authentication guard. Sanctum uses the `Sanctum` guard which looks up the token in the `personal_access_tokens` or `sanctum_tokens` tables, validates scopes, and then attaches the corresponding user model to the request. Passport uses the `passport` guard (or `oauth` guard) which validates the access token against the `oauth_access_tokens` table, checks its revocability, and resolves the token’s owner, often a user or a client.

Sanctum’s architecture is intentionally flat: after token validation, the request proceeds through any defined middleware stacks. This simplicity reduces overhead and eliminates the need for additional OAuth 2.0 introspection endpoints. Passport’s architecture includes an extra layer of abstraction: the OAuth 2.0 introspection endpoint can be used to validate tokens server‑side, and there are routes for token issuance, revocation, and client management. This extra complexity can be beneficial for compliance requirements but adds latency and operational overhead.

From a deployment perspective, both packages can be scaled horizontally because token validation is read‑heavy. Sanctum benefits from caching strategies such as Redis or Memcached to speed up token look‑ups. Passport can also use caching for client and scope data, but the revocation table often requires a fast key‑value store to maintain low latency when revoking tokens in real time.

Finally, consider how the chosen package integrates with your existing Laravel application stack. Sanctum works out of the box with Laravel Breeze, Jetstream, and other authentication scaffolding, providing seamless integration for SPA and mobile flows. Passport is more standalone, often requiring custom front‑end logic for authorization redirects and token storage. Understanding these architectural nuances helps you align the authentication choice with your development workflow, team expertise, and long‑term maintenance strategy.

Step-by-Step Guide

Start by installing a fresh Laravel project using Composer: `composer create-project laravel/laravel blog-app`. Navigate into the project and configure your database credentials in `.env`. Next, run migrations to create the necessary tables for your chosen package.

For Sanctum, enable the package via `composer require laravel/sanctum` and run `php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"` to publish its configuration file. In `config/sanctum.php`, set the `stateful` and `protected` arrays according to your client origins. Then run `php artisan migrate` and add `Laravel\Sanctum\Middleware\AuthenticateWithToken::class` to your `api` middleware group. After that, you can generate a token for a user using `$user->createToken('api-token')->plainTextToken`. Use this token in subsequent requests via the `Authorization` header.

Protecting routes with Sanctum is straightforward. Define a route inside `routes/api.php` and apply the `auth:sanctum` middleware: `Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); })`. If you need to restrict access based on scopes, you can use `$request->user()->tokenCan('posts:read')` inside the controller. You can also use `Ability` middleware provided by Sanctum to enforce scopes at the route level.

To implement Passport, first add the package with `composer require laravel/passport`. Run `php artisan vendor:publish --provider="Laravel\Passport\PassportServiceProvider"` to publish config and migration files, then run `php artisan migrate`. After that, run `php artisan passport:install` which creates a personal access client, a password grant client, and generates a unique client ID and secret. In `config/passport.php`, you can configure auth providers and token expiration. Next, add the `Laravel\Passport\Http\Middleware\CreateFreshApiToken::class` to your `api` middleware group if you want to automatically attach a token to every authenticated request.

Passport issues tokens following the OAuth 2.0 authorization code flow. Clients must request authorization, users log in, and the client receives an authorization code that is swapped for an access token. For simplicity, many APIs use the password grant or client credentials grant. To protect a route, use the `auth:passport` guard: `Route::middleware('auth:passport')->post('/posts', 'PostController@store')`. The `$request->user()` call returns the authenticated user or client, depending on the guard. You can also validate scopes via `Gate::allows` or custom policies.

After implementing both systems, it’s good practice to test them with tools like Postman or built‑in Laravel stubs. Use the `php artisan tinker` console to simulate token generation and route access. Document the process for your team, and consider writing unit tests for your authentication logic using Laravel’s `TestCase` and `CreatesApplication` helpers.

Real-World Examples

A typical Single-Page Application (SPA) built with Vue or React often uses Sanctum because it can accept tokens via the Authorization header without needing a CSRF token. In this scenario, you configure Sanctum’s `stateful` array with your app’s origin, enable token authentication, and have the client store the plain text token in local storage or memory. The SPA then attaches the token to each AJAX request, and Laravel’s Sanctum middleware validates it, allowing seamless CRUD operations without sessions.

Mobile applications, especially those using Flutter or Swift, may prefer Passport for its support of OAuth 2.0 refresh tokens and client credentials. The mobile app obtains an initial access token via the password grant, stores both the access token and a refresh token securely (e.g., using Keychain or EncryptedSharedPreferences), and periodically refreshes the access token using the refresh token. This flow reduces the need for users to re-enter credentials and keeps the app authenticated over long periods.

Headless CMS integrations often require a service account that can read and write content without user interaction. In such cases, you can generate a personal access token for a “–” created in the database and use it to authenticate API calls from your CMS backend. Both Sanctum and Passport can serve this purpose, but Sanctum’s simple token model reduces boilerplate code, making it a favorite for developers building content APIs.

E‑commerce platforms may use both packages for different client types. Sanctum can protect the storefront API that mobile apps consume, while Passport secures the admin API that third‑party integrators access using OAuth 2.0. By separating the authentication mechanisms, you can apply different policies and token lifetimes to each client category, improving overall security posture.

Production Code Examples

Below is a minimal Sanctum configuration that you can paste into `config/sanctum.php`. This example assumes your SPA runs on `https://example.com` and your mobile app on native schemes.


return [
    'stateful' => explode(',', env('SANCTUM_STATEFUL', 'http://localhost,http://localhost:8080,https://example.com')),
    'protected' => ['http://localhost:8080', 'https://example.com'],
    'guard_name' => 'sanctum',
    'authentication' => true,
];

Then in `bootstrap/app.php`, add the token authentication middleware:


->withMiddleware(function (Middleware $middleware) {
    $middleware->api(prepend: [
        Laravel\Sanctum\Middleware\AuthenticateWithToken::class,
    ]);
}),

Here is an example of generating a token for a user in a seed file or artisan command:


$user = User::find(1);
$token = $user->createToken('api-token-for-admin', ['posts:read', 'posts:write']);
echo $token->plainTextToken;

And a protected route that enforces scopes:


Route::middleware('auth:sanctum')->get('/posts', function (Request $request) {
    if (! $request->user()->tokenCan('posts:read')) {
        return response()->json(['message' => 'Access denied'], 403);
    }
    return Post::all();
});

For Passport, the minimal setup involves publishing config and installing clients. The `config/passport.php` can be left with default values unless you need custom expiration or scopes. Below is a snippet that creates a token using the password grant:


$client = OAuthClient::where('personal_access_client', 1)->first();
$token = $client->tokens()->create([
    'user_id' => 1,
    'name' => 'Mobile API Token',
    'scopes' => '',
    'revoked' => false,
]);
echo $token->access_token;

To protect routes with Passport, you can use the `auth:passport` guard:


Route::middleware('auth:passport')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});

Optionally, add the `CreateFreshApiToken` middleware to automatically attach a token to API requests:


->withMiddleware(function (Middleware $middleware) {
    $middleware->api(prepend: [
        Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
    ]);
}),

Comparison Table

Feature Sanctum Passport
Focus Simple token authentication Full OAuth 2.0 authorization server
Installation One composer require, publish config Two packages, publish config and migrations, run passport:install
Token Types API tokens attached to users or –s Access tokens and refresh tokens
Scope Support String scopes, tokenCan check OAuth scopes, server‑side enforcement
State Management Stateless, middleware only Supports stateful sessions and token revocation
Performance Lower latency, no OAuth introspection Higher overhead due to OAuth flow
Use Cases SPAs, mobile apps, HEADLESS CMS Third‑party app integrations, legacy OAuth needs
Security Features Token revocation, CORS config Refresh token rotation, revocation table

Best Practices

Always limit token scopes to the minimum required for a given task. Over‑privileged tokens increase risk if leaked. For Sanctum, you can assign scopes like “posts:read” and check them with `$request->user()->tokenCan('posts:read')`. In Passport, scopes are defined in the `oauth_scopes` table and enforced during token issuance.

Set appropriate expiration times for access tokens. Sanctum tokens can be configured via `SANCTUM_EXPIRATION` in minutes, while Passport allows `exp` configuration in `config/passport.php`. Short‑lived tokens reduce the window of exposure and encourage the use of refresh tokens where applicable.

Store token secrets securely. For Sanctum, the plain text token is returned once; treat it like a password and avoid exposing it in logs. Passport stores a secret in the `oauth_clients` table; never commit this secret to version control. Use environment variables and rotate secrets when necessary.

Implement rate limiting on authentication endpoints. Both packages expose routes for token issuance; limiting requests prevents brute‑force attacks. Laravel’s `throttle` middleware can be applied to routes under `routes/auth.php` or custom controllers.

Use HTTPS in production and configure CORS appropriately. Sanctum’s `protected` array can restrict which origins are allowed to send credentials. Passport also respects CORS settings defined in your application, but you should enforce same‑origin policies for sensitive token endpoints.

Common Mistakes

Mixing Sanctum and Passport in the same application leads to configuration conflicts and confusing token validation. Choose one based on your OAuth needs and keep the authentication guard consistent across routes.

Neglecting to assign scopes leaves tokens with unlimited permissions. Developers often forget to add scopes when creating tokens, allowing unintended actions. Always define and enforce scopes from the start.

Exposing client secrets in front‑end code or logs is a frequent oversight. In Passport, the client secret is needed for confidential clients; keep it server‑side only. For Sanctum, while the token itself is sent to the client, never log it.

Not configuring CORS correctly results in browsers blocking legitimate requests. The `stateful` and `protected` arrays must include all origins that will call your API, otherwise the Authorization header is stripped by the browser.

Assuming token revocation works out of the box without proper database indexing. Revocation tables can become bottlenecks; ensure you have indexes on token identifiers and use a fast database engine or Redis for caching token status.

Performance Tips

Caching token look‑ups using Redis or Memcached can dramatically reduce database load. In Sanctum, you can enable token caching with `SANCTUM_CACHE` environment variable; in Passport, consider caching scopes and client data.

Keep access tokens short‑lived and rely on refresh tokens to extend sessions. This minimizes the amount of time a compromised token can be used, while reducing the frequency of re‑authentication prompts for users.

Use the `throttle` middleware on token issuance routes to prevent abusive clients from overwhelming the database with token requests. Limit the number of token requests per minute per IP.

Organize your database queries to avoid N+1 issues when loading user data along with token scopes. Use eager loading and join queries where appropriate. Efficient database access directly translates to faster API responses.

Enable HTTP/2 and gzip compression on your Nginx or Apache server. Faster network transport and smaller payload sizes reduce overall response time, especially for token‑heavy endpoints.

Security Considerations

CORS misconfiguration can inadvertently allow malicious sites to read your tokens via JavaScript. Define the `protected` array in `config/sanctum.php` with only trusted origins, and implement similar origin checks for Passport clients.

Never store bearer tokens in local storage if the client is susceptible to XSS. Use HttpOnly cookies for session‑based authentication where applicable, and restrict token exposure to secure contexts only.

Implement token revocation mechanisms. Sanctum provides a `tokens` controller for revoking tokens; Passport includes a revocation endpoint that can be called to invalidate an access token or refresh token.

Use PKCE (Proof Key for Code Exchange) for public clients to mitigate authorization code interception. While Sanctum does not enforce PKCE by default, you can add it on the client side for additional safety.

Apply the principle of least privilege to client registrations. In Passport, limit the scopes granted to each client to only what it needs. Also, rotate client secrets periodically and monitor for abnormal token issuance patterns.

Deployment Notes

Set the `APP_ENV` to `production` and ensure `APP_DEBUG` is false. This prevents leakage of debugging information that could reveal token details. Also, use `APP_URL` to reflect the correct domain for generating absolute URLs.

Configure your web server to use HTTPS. Obtain a valid SSL certificate and redirect all HTTP traffic to HTTPS. Laravel’s `forceHttps` middleware can be added to ensure all API responses are sent over secure channels.

Store secrets in environment variables rather than in configuration files. Use `.env` for local development and secret management services like HashiCorp Vault or AWS Parameter Store in production. Keep `SANCTUM_SECRET` or `PASSPORT_CLIENT_SECRET` out of version control.

Consider using Redis for caching token validation and session storage. This reduces database load and improves latency, especially under high traffic. Configure `cache.store` to use Redis and enable `SANCTUM_CACHE` if using Sanctum.

Deploy Laravel Horizon for monitoring queue jobs and Laravel Telescope for debugging requests. These tools help you spot unauthorized token usage or unusual patterns quickly after rollout.

Debugging Tips

Enable Laravel Telescope or install a custom log channel that records token IDs and scopes whenever an authentication attempt occurs. Review `storage/logs/laravel.log` for clues about why a request was denied.

When using Sanctum, verify that the `Authorization` header is correctly formatted as `Bearer `. Many client libraries automatically prepend the bearer schema, but manual requests can omit it. Use Postman to test the exact header format.

For Passport, check the `oauth_access_tokens` table to confirm the token exists and has not been revoked. Also, ensure the token’s `scopes` column matches the required permissions for the endpoint.

Use the `php artisan route:list` command to confirm which middleware is attached to each route. If you accidentally applied `auth:passport` to a route protected by Sanctum, the request will fail. Align your guard names with your package choice.

Set up a simple health check endpoint that returns the authenticated user’s ID using the correct guard (e.g., `auth:sanctum`). Call this endpoint from your client during startup to verify connectivity and token validity.

FAQ

What is the main difference between Laravel Sanctum and Laravel Passport?

Sanctum focuses on simple token authentication with optional scopes, making it a lighter solution for SPAs and mobile apps, while Passport implements the full OAuth 2.0 protocol, providing an authorization server with client registration, authorization codes, and refresh tokens. Choose Sanctum for ease of use and performance, and Passport when you need strict OAuth compliance or third‑party app integrations.

When should I use Sanctum over Passport?

Use Sanctum when you are building a straightforward API that does not require the complexity of OAuth 2.0 flows. Ideal for single‑page applications, mobile apps, or headless CMS integrations where you simply need a bearer token for authentication and fine‑grained scopes.

How do I install Laravel Passport in a fresh project?

Run `composer require laravel/passport`, publish its providers and migrations via artisan commands, then execute `php artisan migrate` and `php artisan passport:install`. This creates a personal access client and a password grant client, providing you with client ID and secret for OAuth flows.

Can I use both Sanctum and Passport in the same Laravel application?

It is possible, but not recommended, because they use different authentication guards and token tables, which can lead to confusion and security oversights. It is better to standardize on one package to keep your authentication logic consistent and maintainable.

How are scopes handled in Sanctum vs Passport?

In Sanctum, scopes are simple strings attached to a token and checked at runtime using `$request->user()->tokenCan('scope')`. Passport scopes are stored in the `oauth_scopes` table and enforced during token issuance, aligning with OAuth 2.0 specifications.

What is token revocation and why is it important?

Token revocation invalidates a token before its natural expiration, which is crucial when a token is compromised or a user logs out. Both Sanctum and Passport provide mechanisms to revoke tokens, preserving security.

How can I protect my API from CSRF attacks?

Both Sanctum and Passport are designed to be stateless and do not rely on session cookies. Use the `AuthenticateWithToken` middleware and ensure your API only accepts requests with the `Authorization` header, mitigating CSRF risks. Additionally, enforce CORS policies to restrict cross‑origin requests.

What are common performance pitfalls when using these packages?

Not caching token lookups, using overly broad scopes, failing to limit token expiration, and calling heavy database queries during authentication. Implement Redis caching, define minimal scopes, set short token lifetimes, and optimize database queries to keep API performance high.

Conclusion

Choosing between Laravel Sanctum and Passport hinges on the specific requirements of your project, the complexity of your authentication flow, and the performance expectations of your users. Sanctum provides a streamlined, low‑overhead solution perfect for modern SPAs and mobile clients, while Passport offers the full OAuth 2.0 ecosystem for legacy systems and third‑party integrations. By following the step‑by‑step guide above, applying best practices, and being aware of common pitfalls, you can confidently implement secure token authentication that scales with your application. Evaluate your client types, security policies, and development timeline, and let the appropriate package guide you toward a robust and maintainable API. For further reading, refer to the official Laravel documentation for each package, and consider experimenting with both approaches in a sandbox environment before committing to a production setup.