sukhchain's avatar

Laravel Sanctum Optional Auth on Route

I am having many routes which use auth:sanctum middleware

Route::group(['middleware' => ['auth:sanctum']], function () {
	Route::get('/user', 'Api\AppController@user');
	//... and so on
});

Now i have one route which unauthenticated users can also access. On this route i'm not using auth:sanctum to allow access for guest users. But what if any authenticated user comes? I'm not able to execute function for them. Is there any way to enable optional auth for specific routes?

if (Auth::user()) {
	//Unable to access auth status
	//Execute something...
}

If i add auth:sanctum to this route then guest users are unable to access this API too. :(

0 likes
8 replies
ipearx's avatar

I too would like a solution to this. I didn't have this problem using passport from memory?

2 likes
bpuig's avatar

Use auth('sanctum')->user() and remove middleware from route.

9 likes
Cellard's avatar
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class OptionalAuthSanctum
{
    public function handle(Request $request, Closure $next)
    {
        if ($request->bearerToken()) {
            Auth::setUser(
                Auth::guard('sanctum')->user()
            );
        }

        return $next($request);
    }
}
9 likes
Khalyst_0's avatar

@cellard 's solution works for me except for one piece, where an invalid or non-existent token still persisted as an error in the Handler. Resolved by updating the conditional body with an immediate user check: if (Auth('sanctum')->user()). Seems to be working as intended, where an underlying user token is completely optional to access the route through the middleware.

2 likes
rajabilal555's avatar

The way I like it, is to extend the base Authenticate Middleware from laravel's core, but override the unauthenticated method to not throw the exception. Works good in my apis

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class OptionalAuth extends Middleware
{
    /**
     * Handle an unauthenticated user.
     *
     * @param \Illuminate\Http\Request $request
     * @param array $guards
     * @return void
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    protected function unauthenticated($request, array $guards)
    {
        // do nothing, but set the default guard through parent `authenticate` method...
    }
}

Next, add the alias according to your laravel version... i.e. Laravel 11:

        $middleware->alias([
            'optional_auth' => OptionalAuth::class
        ]);

Then you can use it same as the normal auth middleware...

Route::middleware('optional_auth:sanctum')->group(function () {
    // Optional Authenticated routes
    Route::get('example', [Api\ExampleController::class, 'index']);
});
bloodykheeng's avatar

cerate middleware php artisan make:middleware OptionalAuthSanctum

middle ware should be like this

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class OptionalAuthSanctum
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        // Attempt to authenticate only if a bearer token is provided
        if ($request->bearerToken()) {
            $user = Auth::guard('sanctum')->user();
            if (isset($user)) {
                Auth::setUser($user);
            }
        }

        return $next($request);
    }
}

Then go and register it bootstrap/app.php

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        commands: __DIR__ . '/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {

        // Register the middleware alias
        $middleware->alias([
            'optional_auth' => \App\Http\Middleware\OptionalAuthSanctum::class,
        ]);

    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

then go and use it for optional routes in api.php eg

// feedback
Route::post('feedback', [FeedbackController::class, 'store'])->middleware('optional_auth'); //otional auth is a custom middle i craeted myself

Please or to participate in this conversation.