Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Build a REST API with Laravel Sanctum Authentication

By Kokil Thapa | Last reviewed: August 2026

If you need to build a REST API with Laravel Sanctum authentication, you are choosing the standard for modern PHP backends in 2026. Sanctum provides a lightweight, dual-mode authentication system that handles both stateful SPA sessions and stateless mobile tokens without the complexity of OAuth2. This guide walks through the exact configuration, code patterns, and security considerations required to ship a production-grade authenticated API using Laravel 12 and PHP 8.4.

How Do You Configure Laravel Sanctum for REST API Authentication?

Before writing any controller logic, you must correctly wire Sanctum into your Laravel application. While newer versions of Laravel include Sanctum by default, verifying the configuration is critical because misconfigured guards are the most common reason authentication fails silently. For a comprehensive overview of backend setup, refer to this guide on hiring or working as a Laravel developer in Nepal, which covers environment expectations.

Installation and Migration

Ensure you are running PHP 8.2+ and Laravel 11 or 12. Install Sanctum via Composer and publish its migration files:

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

This creates the personal_access_tokens table. In production, never skip the migration step even if you think the table exists; schema drift between environments causes subtle token lookup failures.

Configuring the Auth Guard

Open config/auth.php and ensure your api guard uses the sanctum driver. In Laravel 12, this is often pre-configured, but always verify:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'sanctum',
        'provider' => 'users',
    ],
],

Next, add the Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful middleware to your bootstrap/app.php (or app/Http/Kernel.php in older structures) within the api middleware group. This single line enables Sanctum’s dual-mode magic: it allows cookie-based session auth for SPAs on the same domain while falling back to Bearer tokens for mobile apps.

SPA Client(Same Domain)Session CookieMobile App(Cross-Origin)Bearer TokenSanctum MiddlewareDetect Request TypeRoute: auth:sanctumProtected APIUser Context$request->user()
Sanctum resolves authentication differently based on client type while exposing a unified user context to your API controllers.

User Model Configuration

Your User model must use the HasApiTokens trait. Without this, token creation methods will not exist:

use Laravel\Sanctum\HasApiTokens;

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

In my experience maintaining legal-tech portals like Court Marriage In Nepal, forgetting this trait during an upgrade from Passport to Sanctum caused hours of debugging. Always verify the trait exists after framework upgrades.

How Do You Issue and Manage Personal Access Tokens?

The core of building a REST API with Laravel Sanctum authentication is the token issuance endpoint. Unlike JWT systems where tokens are signed locally, Sanctum tokens are opaque strings stored in the database. This makes revocation instant and reliable.

Creating the Login Endpoint

Create a dedicated controller for authentication. Never mix login logic with general user management:

public function login(Request $request)
{
    $credentials = $request->validate([
        'email' => ['required', 'email'],
        'password' => ['required'],
        'device_name' => ['required', 'string', 'max:255'],
    ]);

    if (!Auth::attempt($credentials)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

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

    return response()->json([
        'token' => $user->createToken(
            $request->device_name,
            ['read', 'write'] // Token abilities
        )->plainTextToken,
        'user' => $user,
    ]);
}

Note the device_name parameter. Requiring this prevents users from having indistinguishable tokens when they log in from multiple devices. On real client projects, I’ve seen support tickets vanish simply because we could identify which device held a compromised token.

Understanding Token Abilities

Abilities are Sanctum’s permission system. They are not roles; they are scopes attached to a specific token. When creating a token, pass an array of abilities. Check them in middleware or controllers:

// In controller
if ($request->user()->tokenCan('write')) {
    // Allow mutation
}

// In route definition
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:write');

For complex RBAC needs beyond simple scopes, combine Sanctum with Spatie Laravel Permission. Sanctum handles "who is this?" while Spatie handles "what can they do?". This separation keeps your authentication layer clean.

Revoking Tokens

Because tokens are database-backed, revocation is synchronous. Implement logout by deleting the current token:

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

    return response()->json(['message' => 'Logged out']);
}

For administrative interfaces, you can revoke all tokens for a user via $user->tokens()->delete(). This is essential for security incidents or password resets.

What Is the Difference Between Sanctum and Passport for APIs?

Choosing between Sanctum and Passport is a frequent decision point when you build a REST API with Laravel Sanctum authentication. Understanding the trade-offs prevents architectural debt. For deeper comparison, see this detailed breakdown of Laravel Passport vs Sanctum.

FeatureLaravel SanctumLaravel Passport
Authentication TypeAPI Tokens + Session CookiesFull OAuth2 Server
ComplexityLow (minutes to set up)High (requires OAuth2 knowledge)
Token StorageDatabase (opaque)Encrypted JWT / Database
RevocationInstant (DB delete)Requires refresh token handling
Third-Party AuthNo native supportBuilt-in authorization codes
Best ForSPAs, Mobile Apps, Internal APIsPublic APIs, Microservices, SSO
Performance OverheadMinimal (single DB lookup)Higher (encryption/signing)

In practice, 90% of Laravel projects I’ve shipped since 2020 use Sanctum. Passport is reserved for platforms acting as identity providers or requiring third-party OAuth2 flows. If you are building a consumer app, admin panel, or B2B service, Sanctum is the correct default.

Need API Auth?OAuth2 / Third-Party?NOYESUse SanctumSimple, Fast, SecureUse PassportFull OAuth2 Server• SPA / Mobile Apps• Internal Services• Simple Token Auth• Public Developer API• SSO / Identity Provider• Complex Grant Types
Decision framework for selecting the appropriate Laravel authentication package based on architectural requirements.

How Do You Secure Sanctum Endpoints in Production?

Authentication alone does not make an API secure. When you build a REST API with Laravel Sanctum authentication, you must layer additional protections. Security is especially critical for Nepal-based legal-tech and financial applications where data sensitivity is high.

Mandatory HTTPS and CORS

Sanctum tokens are bearer credentials. Transmitting them over HTTP exposes them to interception. Enforce HTTPS at the Nginx/Apache level and redirect all HTTP traffic. Configure CORS strictly in config/cors.php:

'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://yourdomain.com'], // Never wildcard in prod
'supports_credentials' => true,

Setting supports_credentials to true is required for SPA session auth but demands explicit origin whitelisting. Using * with credentials enabled is a security vulnerability that browsers will reject anyway.

Rate Limiting

Protect your login and registration endpoints aggressively. In bootstrap/app.php, configure rate limits:

->withMiddleware(function (Middleware $middleware) {
    $middleware->throttleApi();
})

Customize the throttle key to include IP and email to prevent distributed brute-force attacks. For public-facing APIs, consider implementing the patterns described in this guide to API rate limiting and abuse prevention.

Token Hygiene

Tokens accumulate over time. Implement automated cleanup:

  • Schedule a weekly command to delete expired or unused tokens
  • Set reasonable expiration dates during token creation ($user->createToken(..., expires_at: now()->addMonth()))
  • Force re-authentication for sensitive operations regardless of token validity
  • Log token creation events for audit trails

On a recent e-commerce project, we discovered hundreds of orphaned tokens from a deprecated mobile app version. A scheduled cleanup task reduced database bloat and improved token lookup performance by 15%.

MaliciousRequestHTTPS/TLSEncrypt TransitCORSOrigin CheckRate LimitThrottle AbuseValidationInput SanitizeSanctumToken VerifyAbilitiesScope CheckSecureResponse
Defense-in-depth architecture for Sanctum-protected APIs showing sequential security validation layers.

How Do You Test and Debug Sanctum Authentication?

Testing authenticated endpoints requires proper setup. A common mistake is testing token endpoints without first creating a valid token in the test database.

Feature Testing with ActingAs

Laravel provides testing helpers specifically for Sanctum. Use actingAs with the Sanctum guard:

use Laravel\Sanctum\Sanctum;

public function test_user_can_access_protected_route()
{
    $user = User::factory()->create();
    
    Sanctum::actingAs($user, ['read']);

    $response = $this->getJson('/api/profile');

    $response->assertStatus(200)
             ->assertJson(['email' => $user->email]);
}

Always specify abilities in tests if your controller checks them. A test passing without abilities may mask authorization bugs that only surface in production.

Debugging Token Issues

When authentication fails silently, check these in order:

  1. Guard Configuration: Ensure auth:sanctum matches your config/auth.php guard name exactly
  2. Middleware Order: The stateful middleware must precede auth middleware in the stack
  3. Token Format: Verify the client sends Authorization: Bearer <token> with a space after Bearer
  4. Database Connection: Confirm the personal_access_tokens table exists in the active database
  5. User Provider: Ensure the provider in auth.php matches your User model namespace

Enable query logging temporarily to see if Sanctum is actually querying the tokens table. If no query runs, the middleware is not being applied. If a query runs but returns null, the token is invalid or expired.

Build a REST API with Laravel Sanctum Authentication for Production

Successfully deploying a REST API with Laravel Sanctum authentication requires attention to configuration details that documentation often glosses over. Start with the correct guard setup, implement strict token issuance with device tracking, layer security controls beyond basic auth, and maintain token hygiene through automated processes. Whether you are building a legal-tech portal in Kathmandu or a global SaaS platform, these patterns scale reliably.

If you need hands-on implementation support or a technical audit of your existing Laravel API, reach out to discuss your project requirements. I regularly help teams ship secure, production-ready authenticated APIs using modern Laravel patterns.

Frequently Asked Questions

Laravel Sanctum is a lightweight authentication package for SPAs, mobile apps, and simple token-based APIs. It provides both session-based auth for same-origin frontends and personal access tokens for third-party integrations without OAuth complexity.

Run composer require laravel/sanctum, publish the config with php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider", run migrations, then add EnsureFrontendRequestsAreStateful middleware to your api group in bootstrap/app.php or routes/api.php depending on configuration style.

Yes. Sanctum handles cookie-based session authentication for same-domain SPAs and stateless bearer token authentication for mobile or external clients within the same application, using separate guards and middleware configurations defined in config/sanctum.php and config/auth.php.

Laravel 12 requires PHP 8.2 or higher. PHP 8.3 and 8.4 are fully supported and recommended for production. Sanctum itself has no additional PHP requirements beyond the framework baseline, so any PHP version compatible with Laravel 12 works identically for API authentication.

For a standard CRUD API with Sanctum auth, expect NPR 80,000–200,000 (USD 600–1,500) depending on endpoint count and integration complexity. This covers setup, testing, and documentation but excludes ongoing maintenance or third-party payment gateway integration like eSewa or Khalti.

Remove EnsureFrontendRequestsAreStateful from your API middleware stack and rely solely on token guards. Configure config/auth.php defaults guard to sanctum, ensure User model uses HasApiTokens trait, and issue tokens via $user->createToken(). All requests must include Authorization: Bearer header.

Common causes include missing HasApiTokens trait on the User model, incorrect guard configuration in config/auth.php, expired or revoked tokens, or sending tokens without the Bearer prefix. In my experience debugging production APIs, mismatched SANCTUM_STATEFUL_DOMAINS environment variables also cause silent failures when mixing session and token auth.

Yes, but you must configure SANCTUM_STATEFUL_DOMAINS in .env to include the subdomain, set SESSION_DOMAIN to the parent domain, and ensure CORS allows credentials. The SPA must make an initial GET request to /sanctum/csrf-cookie before authenticated requests. I have implemented this pattern on legal-tech portals serving Vue dashboards alongside Laravel APIs.

Call $token->delete() or $user->tokens()->where('id', $tokenId)->delete() to revoke individual tokens. For expiration, check created_at against your policy in middleware since Sanctum tokens lack native expiry. On production systems I maintain, we store last_used_at and run scheduled cleanup jobs to purge stale tokens older than 90 days.

Sanctum is secure when properly configured with HTTPS, token scoping, and rate limiting. For legal-tech portals handling sensitive documents, I combine Sanctum with IP allowlisting, audit logging, and short-lived tokens. Never store tokens in localStorage; use httpOnly cookies for SPAs or secure storage for mobile apps. Always validate permissions server-side regardless of frontend claims.

Sanctum is simpler, lighter, and sufficient for most projects needing personal access tokens or SPA auth. Passport implements full OAuth2 with authorization codes, client credentials, and refresh tokens. Choose Passport only when integrating third-party OAuth providers or building public APIs requiring delegated authorization. For internal APIs and client portals, Sanctum reduces complexity significantly.

Sanctum creates personal_access_tokens table with columns for tokenable morphs, name, token hash, abilities, and timestamps. Add composite indexes on tokenable_type/tokenable_id for user lookups and a unique index on the token column for fast bearer validation. On high-traffic APIs, consider partitioning by created_at if token volume exceeds millions of rows.

Pass abilities array when creating tokens: $user->createToken('mobile', ['orders:read', 'profile:update']). Check abilities in controllers using $request->user()->tokenCan('orders:read'). Combine with Spatie Laravel Permission for role-based access control. In practice, I define ability constants in a dedicated class to avoid string typos across endpoints and tests.

Yes. Create dedicated payment endpoints protected by Sanctum tokens or session auth. Verify webhook signatures server-side independently of Sanctum since gateways call back without user context. Store transaction records linked to authenticated users via foreign keys. On Nepalese eCommerce projects, I isolate payment verification logic in service classes testable without live gateway credentials.

Ensure APP_URL matches your production domain exactly including https protocol. Set SANCTUM_STATEFUL_DOMAINS correctly in .env after deployment. Configure PHP-FPM opcache to revalidate files after deploys using Deployer 7 symlink swaps. Verify SSL termination happens before Laravel if using reverse proxies. I have seen 401 errors persist for hours due to cached config values not being cleared during zero-downtime deployments.

Share this article

Quick Contact Options
Choose how you want to connect me: