Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

HallOfCode's avatar

Creating a combine Auth:api/Sanctum and Auth:web Middleware / use One or the other Middleware?

I want to create an "OR" Middleware, so I want to protect a Route by one OR another middleware. So my Route /api/v1/example should be accessible when I'm authenticated via Login Form in my Webbrowser, aswell as over a Sanctum-Token Authentication thought Postman.

The only possible Solution which I've know is to copy/recreate the Auth:api/and sanctum aswell as the Auth(::web) put both into a new Middleware, and do it via If ... else if ... .

Did somebody has any other idea how to do something like that?

0 likes
1 reply
HallOfCode's avatar

=========================================================

Edit: Found a better solution:

In Laravel we don't have something like use this one OR use this other middleware, so we had to create a new middleware (in this example my_new_middleware.php). But I dont know any way to implement two existing middlewares in one file and combine them... so we had to rewrite the functionalitties the easiest way - which is auth()->check() OR Auth::check() - this let us check if the user is authenticated (as a specific guard). So we check if the user is authenticated over Sanctum-API by using:

if ( auth("sanctum")->check() ) { ... }

Then we could set the default guard by using:

auth()->shouldUse('guardname');

After that, it should looks like this:

my_new_middlware.php

public function handle(Request $request, Closure $next)
{
        if ( auth('sanctum')->check())
        {
            auth()->shouldUse('sanctum'); //api sanctum auth
            return $next($request);
        }
        else if(auth()->check())
        {
            auth()->shouldUse('web');       //normal user auth
            return $next($request);
        }
        return response()->json(['error' => 'An error occured.']);
}

Now we can set the middleware to any route. And this routes then should accessible aswell for normal Email/Password users* aswell as Sancum API users.

Notice

By default every routes in api.php are protected by the auth:api middleware, and web.php is protected by auth:web (or just normal auth) middleware. To make a route in api.php accessible for Email/Password web users we had to set the api guard in the Boot()-function RouteServiceProvider.php as follows:

public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware("web") //change this one from "api" to "web"
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware("web")
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }

( look: ->middleware("web") //change this one from "api" to "web")

Now all POST routes in api.php are CSRF protected, to remove this protection, we need to add the url-path to VerfifyCsrfToken.php like this:

protected $except = [
        '/api/*'
];

to remove all /api/... routes from CSRF protection.

This is how my api.php looks like at the end:

//this routes are accessible for normal users aswell as api users
Route::middleware(["my_new_middleware"])->group(function () {
     Route::prefix('/both')->group(function () {
           Route::get('/some', [accessController::class, 'someData']);
           Route::post('/other', [accessController::class, 'someCreate']);
     });
});

//this routes are accessible only for api users (to keep the original functionallity of api.php)
Route::middleware(["auth:api", "auth:sanctum"])->group(function () {
     Route::prefix('/onlyapi')->group(function () {
           Route::get('/xy', [accessController::class, 'apiData']);
           Route::post('/abc', [accessController::class, 'apiCreate']);
     });
});

I hope that helps the few people who will visit this site in the next few years after googeling for houres :D

===========================================================

Really bad and Experimental Solution (use the other solution recommended)

Created a new Middleware, which is checking if user is logged in over normal login or with Sanctum API Token:

//my middleware

public function handle(Request $request, Closure $next)
{
        if ( auth('sanctum')->check())
        {
            $request['__current_guard'] = 'sanctum';
            return $next($request);
        }
        else if(auth()->check())
        {
            $request['__current_guard'] = '';
            return $next($request);
        }
        return response()->json(['error' => 'User Authentication isnt defined.']);
}

Then I store into this middleware a new key into the $request which you do like this:

//first way
$request['mykey'] = 'myvalue';

// second way
Request()->request->add(['mykey' => 'myvalue']);

In the controller know I can access Request('__curent_guard'); , which should be 'sanctum' if logging in via Token, and an empty string when we logged in the default way.

//my controller
public function getUserName(): string
{
	    $user_name = auth( Request('__current_guard') )->user()->name;
		return $user_name;
}

But notice that this is a really Experimental and defenitly not Recommended way of doing this.

2 likes

Please or to participate in this conversation.