Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 REST API Authentication with Sanctum: Step-by-Step Tutorial

๐Ÿ› ๏ธ Laravel Tutorials4 min read

Laravel 13 REST API Authentication with Sanctum: Step-by-Step Tutorial

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 8, 2026
Laravel 13 REST API Authentication with Sanctum: Step-by-Step Tutorial

To build a REST API with token authentication in Laravel 13, install Sanctum with php artisan install:api, add the HasApiTokens trait to your User model, issue tokens with createToken(), and protect routes with the auth:sanctum middleware. Clients then send the token as a Bearer header. This guide builds a small login, profile and logout API and shows how to scope tokens with abilities.

What do you need before you start?

  • Laravel 13 (it requires PHP 8.3 or newer; PHP 8.3 to 8.5 are supported)
  • A database connection and a users table (a fresh Laravel app has both)
  • An HTTP client such as curl, Postman or the Laravel test client

How do you install Sanctum for API tokens?

Run one Artisan command. It publishes the API route file, creates the personal_access_tokens migration and prepares the app for token auth.

Code
php artisan install:api
php artisan migrate

Then add the trait to the User model so it can create and own tokens.

PHP
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

How do you issue a token on login?

Validate the credentials, check the password hash, then return the plain-text token. Sanctum stores only a SHA-256 hash, so the plain-text value is available once, at creation time.

JavaScript
<?php

namespace App\Http\Controllers\Api;

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

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $data = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
            'device_name' => ['required', 'string', 'max:100'],
        ]);

        $user = User::where('email', $data['email'])->first();

        if (! $user || ! Hash::check($data['password'], $user->password)) {
            throw ValidationException::withMessages([
                'email' => ['The provided credentials are incorrect.'],
            ]);
        }

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

    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();

        return response()->noContent();
    }
}

How do you protect API routes?

Attach the sanctum guard through the auth:sanctum middleware in routes/api.php.

JavaScript
<?php

use App\Http\Controllers\Api\AuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/login', [AuthController::class, 'login']);

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

How do token abilities work?

Abilities act like scopes. Pass them as the second argument of createToken(), then check them with tokenCan() or with route middleware.

PHP
$token = $user->createToken('mobile', ['orders:read'])->plainTextToken;

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

To use the middleware, register the aliases in bootstrap/app.php, then use abilities:a,b (all required) or ability:a,b (any one).

JavaScript
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'abilities' => CheckAbilities::class,
        'ability' => CheckForAnyAbility::class,
    ]);
})
CSS
Route::get('/orders', [OrderController::class, 'index'])
    ->middleware(['auth:sanctum', 'abilities:orders:read']);

How do you expire and revoke tokens?

Tokens never expire by default. Set the expiration option (in minutes) in config/sanctum.php, or pass an expiry as the third argument of createToken(). Schedule sanctum:prune-expired to clean up old rows.

PHP
$user->createToken('cli', ['*'], now()->plus(weeks: 1));

// revoke everything for a user
$user->tokens()->delete();

How do you verify it works?

Call the API with curl. The login response contains the token. Use it on a protected route.

Shell
curl -X POST http://localhost:8000/api/login \
  -H "Accept: application/json" \
  -d "email=user@example.com&password=secret&device_name=curl"

curl http://localhost:8000/api/user \
  -H "Accept: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN"

For automated tests, Sanctum::actingAs(User::factory()->create(), ['orders:read']) authenticates a request with the abilities you list.

What are the common mistakes?

  • Forgetting Accept: application/json, which makes failed auth redirect instead of returning 401
  • Using API tokens for your own first-party SPA. The Sanctum docs recommend its cookie-based SPA authentication for that case
  • Logging or storing the plain-text token. Show it once and discard it
  • Granting ['*'] abilities to every token instead of the minimum needed

FAQ

Is Sanctum or Passport better for a simple API?

Sanctum is the lighter choice for first-party apps and personal access tokens. Choose Passport only when you need a full OAuth2 server.

Does a Sanctum token expire?

Not by default. Configure expiration or set a per-token expiry.

How do I log a user out of one device?

Delete the current token with $request->user()->currentAccessToken()->delete().

References

Article Index & Topic Tags
LaravelAuthenticationApiSanctum

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.