aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

Laravel Sanctum: API Authentication Guide

Laravel Sanctum is the simplest way to add authentication to a modern API. Without the complexity of JWT libraries or the heavy setup of OAuth, it solves two needs in a single package: API tokens for mobile apps and third-party clients, and cookie-based sessions for SPAs like React or Vue running on the same domain. In this article we set up both approaches with real, working code.

When and why Sanctum?

The Laravel ecosystem offers three main authentication options: Passport for projects that need a full OAuth2 server, external libraries for stateless JWTs, and Sanctum, the lightweight solution most applications actually need. Sanctum is the right choice if you are in one of these situations:

  • You have a mobile app or CLI tool and want to attach a simple Bearer token to each request.
  • Your frontend (SPA) runs on the same top-level domain as your Laravel API and you don't need a real OAuth flow.
  • You want to grant ability-based permissions to tokens — for example, a token that can only read.

If you are building a platform open to external developers that requires OAuth2 flows (authorization code, client credentials), Passport is a better fit. For most practical scenarios, however, Sanctum is both faster to set up and lower maintenance.

Installation

In Laravel 11 and 12, Sanctum ships through the install:api command in a single step. This command publishes the package, adds the migration, and prepares the API route file:

php artisan install:api

After the command runs, the migrations are already executed and the personal_access_tokens table is created. All you need to do is add the HasApiTokens trait to your user model:

use Laravel\Sanctum\HasApiTokens;

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

This trait gives the model the createToken() method and the tokens() relationship. We can now issue tokens.

Issuing and using API tokens

The most common scenario: a user logs in with email and password, and you return a token. The client stores it and sends it on subsequent requests via the Authorization: Bearer <token> header. A simple login controller looks like this:

use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Hash;
use App\Models\User;

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)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

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

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

There is an important detail here: plainTextToken returns the plain value of the token only at the moment it is created. The database stores only a hash of the token, so if you lose this value you cannot recover it — the client must save it immediately.

Protect your routes with the auth:sanctum middleware. In routes/api.php:

use Illuminate\Support\Facades\Route;

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

On the client side the request looks like this:

curl https://api.example.com/api/user \
  -H "Authorization: Bearer 1|abcDEF..." \
  -H "Accept: application/json"

Token abilities

Sanctum lets you assign specific abilities to each token. This is a clean way to limit what a token can do:

$token = $user->createToken('report-bot', ['post:read'])->plainTextToken;

Inside a controller or route you can check the ability:

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

You can also add the abilities and ability middleware to routes to restrict access at the gate level. Keep in mind that abilities are only a convenience layer; you still need to enforce the real authorization logic in your application.

SPA authentication

For an SPA running on the same top-level domain, Sanctum uses Laravel's session cookies instead of tokens. This approach is more secure because you don't need to keep the token in localStorage, which JavaScript can access; the browser manages the HttpOnly cookie automatically. The flow is as follows:

  • The SPA first requests a CSRF cookie from the /sanctum/csrf-cookie endpoint.
  • It then sends a normal web request to the /login route; on success a session cookie is set.
  • All subsequent API requests arrive already authenticated through that cookie.

Three settings matter for this to work. First, set supports_credentials to true in config/cors.php. Second, register your SPA's domain in the .env file:

SANCTUM_STATEFUL_DOMAINS=localhost:3000
SESSION_DOMAIN=localhost

Third, the client must send cookies with its requests. If you use Axios:

import axios from 'axios';

axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;

await axios.get('/sanctum/csrf-cookie');
await axios.post('/login', { email, password });
const { data } = await axios.get('/api/user');

The most common mistake here is running the SPA and the API on entirely different domains and then wondering why the session won't stick. The cookie-based method requires both to share the same top-level domain (for example app.example.com and api.example.com).

Revoking tokens and security

When a user logs out or loses a device, you need to invalidate tokens. To delete the current token:

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

To revoke all tokens (log out everywhere):

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

A few security notes: define a token expiration via config/sanctum.php (the expiration key), always run your API behind HTTPS, and add the throttle middleware to your login endpoint to limit brute-force attempts. On the SPA side, having CSRF protection active is the biggest advantage the cookie-based method brings.

Frequently Asked Questions

Should I use Sanctum or Passport?

Unless you are building a full OAuth2 server open to external developers, choose Sanctum. For mobile app tokens and first-party SPA sessions, Sanctum is both lighter and easier to maintain. Passport makes sense when you need OAuth flows like the authorization code grant.

Where should I store tokens?

In mobile apps, keep the token in the platform's secure storage (iOS Keychain, Android Keystore). In SPAs, use cookie-based SPA authentication instead of the token method, so you don't have to keep the token in localStorage, which JavaScript can access.

Why is my SPA login returning a 419 error?

A 419 error is usually a CSRF token mismatch. Make sure you call /sanctum/csrf-cookie before your requests, that the client sends cookies (withCredentials), and that SANCTUM_STATEFUL_DOMAINS and SESSION_DOMAIN are configured correctly.

Want to set up a secure authentication layer for your API? If you need help with Laravel Sanctum integration, SPA sessions, or your mobile APIs, get in touch with me — let's build the solution that fits your project best.

Devamı için