MigSy
MigSy

Reputation: 11

Laravel 11 Sanctum Token Expiration

I'm developing an Api with Laravel 11 using Sanctum as the authentication method. I've set the token expiration to 7 days and I'd like the client to receive a 401 response 'Token invalid or expired' when the token expires. But this doesn't work.Instead, the client (here Postman) receives a 200 response with html content similar to the following

<!DOCTYPE html>
<html lang="en" class="style-basic">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title inertia>Laravel</title>

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

    <!-- Scripts -->
    <script type="text/javascript">
        const Ziggy={"url":"http:\/\/localhost:8000","port":8000,"defaults":{},"routes":{"debugbar.openhandler":{"uri":"_debugbar\/open","methods":["GET","HEAD"]},"debugbar.clockwork":{"uri":"_debugbar\/clockwork\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"debugbar.assets.css":{"uri":"_debugbar\/assets\/stylesheets","methods":["GET","HEAD"]},"debugbar.assets.js":{"uri":"_debugbar\/assets\/javascript","methods":["GET","HEAD"]},"debugbar.cache.delete":{"uri":"_debugbar\/cache\/{key}\/{tags?}","methods":["DELETE"],"parameters":["key","tags"]},"debugbar.queries.explain":{"uri":"_debugbar\/queries\/explain","methods":["POST"]},"l5-swagger.default.api":{"uri":"api\/documentation".....

After some research, I modified the app/Exceptions/Handler file by adding the render function to return the expected response when authentication fails.

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Auth\AuthenticationException;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * A list of exception types with their corresponding custom log levels.
     *
     * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
     */
    protected $levels = [
        //
    ];

    /**
     * A list of the exception types that are not reported.
     *
     * @var array<int, class-string<\Throwable>>
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Throwable  $exception
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function render($request, Throwable $exception)
    {
        if ($exception instanceof AuthenticationException) {
            return response()->json([
                'message' => 'Token expiré ou invalide',
            ], 401);
        }

        return parent::render($request, $exception);
    }
}

This is my api.php content

Route::group([
    'namespace' => 'App\Http\Controllers\Api',
], function () {
    // Route::middleware('api-domain')->group(function () {
        Route::post('user-register', 'AuthenticationController@register')->name('user.account.register');
        Route::post('user-login', 'AuthenticationController@login')->name('user.account.login');

        ....

        Route::middleware('auth:sanctum')->group(function () {
            Route::middleware('active-users')->group(function () {
                
                Route::get('/user', 'AuthenticationController@getUserByToken')->name('user.token');
                Route::get('user-logout', 'AuthenticationController@logout')->name('user.account.logout');
                .....

            });
        });
    // });
});

sanctum.php

/*
    |--------------------------------------------------------------------------
    | Expiration Minutes
    |--------------------------------------------------------------------------
    |
    | This value controls the number of minutes until an issued token will be
    | considered expired. This will override any values set in the token's
    | "expires_at" attribute, but first-party sessions are not affected.
    |
    */

    'expiration' => 7 * 24 * 60,           

app/Http/Kernel.php

/**
     * The application's route middleware groups.
     *
     * @var array<string, array<int, class-string|string>>
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\HandleInertiaRequests::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \App\Http\Middleware\ForceJsonResponse::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            'auth:sanctum',
        ],
    ];

And token creation in my controller

$token = $user->createToken($this->generateTokenName($user), [], Carbon::now()->addMinutes(config('sanctum.expiration')));

Upvotes: 1

Views: 54

Answers (0)

Related Questions