LaravelSanctumAPIAuthenticationSPA

Laravel Sanctum Explained: Simple API Authentication for Modern Apps

Laravel Sanctum makes API authentication simple — whether you need token-based APIs for mobile apps or cookie-based auth for SPAs. Here’s how it works, without the jargon.

Laravel Sanctum Explained: Simple API Authentication for Modern Apps

If you are building an API with Laravel, one of the first questions is: how do users log in securely?

Many developers jump to complex solutions too early. For most apps, Laravel Sanctum is enough.

This post explains Sanctum in plain language — what it is, how it works, and which mode you should use.

What is Laravel Sanctum?

Sanctum is Laravel’s lightweight authentication package. It helps you protect API routes in two ways:

  1. API tokens — for mobile apps and API clients
  2. Session cookies — for first-party SPAs like Vue, React, or Nuxt

It is simpler than Passport. Passport is a full OAuth2 server. Sanctum is for everyday API authentication.

Choose the right tool
─────────────────────
Need full OAuth2 for third-party apps?  → Passport
Need simple API auth for your own apps? → Sanctum

How Sanctum fits in

                Client
         ┌────────┴────────┐
         ▼                 ▼
   API / Mobile         Web SPA
         │                 │
         ▼                 ▼
  Bearer Token         Cookie + CSRF
         │                 │
         └────────┬────────┘
                  ▼
           Laravel Sanctum
                  │
                  ▼
            Protected API

Setup in short

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Then add the trait to your User model:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

That’s the base setup.

Mode 1: Token authentication

Use this when the client sends a token with every request — for example mobile apps or API tools.

Client                         Laravel
  │                               │
  │  POST /api/login              │
  │  email + password             │
  │──────────────────────────────►│
  │                               │
  │  token returned               │
  │◄──────────────────────────────│
  │                               │
  │  GET /api/user                │
  │  Authorization: Bearer token  │
  │──────────────────────────────►│
  │                               │
  │  user data                    │
  │◄──────────────────────────────│

Login example:

public function login(Request $request)
{
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        return response()->json(['message' => 'Invalid credentials'], 401);
    }

    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json([
        'user' => $user,
        'token' => $token,
    ]);
}

Send the token like this:

Authorization: Bearer YOUR_TOKEN_HERE

Protect routes:

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::post('/logout', [AuthController::class, 'logout']);
});

Logout deletes the current token:

$request->user()->currentAccessToken()->delete();

Optional: logout from all devices:

$request->user()->tokens()->delete();

Use this when your frontend and backend belong to the same application family — a browser-based SPA talking to Laravel.

SPA                            Laravel
 │                               │
 │  GET /sanctum/csrf-cookie     │
 │──────────────────────────────►│
 │  CSRF cookie set              │
 │◄──────────────────────────────│
 │                               │
 │  POST /login                  │
 │──────────────────────────────►│
 │  session cookie set           │
 │◄──────────────────────────────│
 │                               │
 │  GET /api/user                │
 │  cookies sent automatically   │
 │──────────────────────────────►│
 │  authenticated response       │
 │◄──────────────────────────────│

Basic flow:

  1. Ask Laravel for a CSRF cookie
  2. Send login credentials
  3. Laravel starts a session
  4. Later API calls use cookies automatically

For this mode, your frontend must send credentials with requests, and your app domains must be allowed in Sanctum’s stateful domains list.

Which mode should you use?

Need authentication?
         │
         ├── Mobile app or pure API client?
         │     → Token authentication
         │
         ├── Browser SPA for your own app?
         │     → Cookie authentication
         │
         └── Both?
               → Sanctum can do both
  • Token auth = easy to test, great for mobile and API clients
  • Cookie auth = natural for browser apps, uses CSRF protection

Protecting API routes

Once Sanctum is in place, protecting routes is straightforward:

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/profile', [ProfileController::class, 'show']);
    Route::apiResource('posts', PostController::class);
});

Only authenticated requests can access these endpoints.

Useful extras

Named tokens help you manage devices:

$user->createToken('mobile-app');
$user->createToken('web-admin');

Token abilities limit what a token can do:

$user->createToken('readonly', ['post:read']);

if ($request->user()->tokenCan('post:read')) {
    // allowed
}

Rate-limit login to reduce brute-force risk:

Route::post('/login', [AuthController::class, 'login'])
    ->middleware('throttle:5,1');

Common mistakes

  • Forgetting the Authorization: Bearer header in token mode
  • Forgetting the CSRF cookie step in SPA mode
  • Not sending credentials in SPA requests
  • Missing Accept: application/json header
  • Confusing Passport and Sanctum use cases
Quick debug path
────────────────
1. Are you using token mode or SPA mode?
2. Token mode → is Bearer token present?
3. SPA mode → was CSRF cookie requested first?
4. Is the route behind auth:sanctum?
5. Is the client sending Accept: application/json?

Final takeaway

Laravel Sanctum keeps API authentication simple.

Use tokens when clients send credentials via headers.
Use cookies when a browser SPA talks to your Laravel backend.
Protect routes with auth:sanctum, and grow from there.

You do not need a complex auth system to build a secure API. In most cases, Sanctum is the clean and practical choice.

Comments

0 comments · new ones appear after approval

No comments yet. Be the first to share your thoughts.

Leave a comment

Your comment will be reviewed before it appears.