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

ignaciodev's avatar

Calling middleware from another middleware

I know this has been discussed before, and most people discourage this, but I wonder what other devs think about this particular case.

I have a resource show route, that can only be accessible by the owner of the resource, or by anyone with a valid signature.

So I created a middleware that checks for the auth(), and to avoid re-creating the handle() method of the ValidateSignature middleware, I just call it from there:

public function handle($request, Closure $next, $relative = null): mixed
    {
        $skip_signature_validation = (
            auth('web')->check() &&
            auth('web')->id() === $request->route('video_call')->user_id &&
        );

        if ($skip_signature_validation) {
            return $next($request);
        }

        return app(ValidateSignature::class)->handle($request, $next, $relative);
    }

Surely, in a case like this it would be okay to call the ValidateSignature middleware from my custom middleware?

What are your thoughs?

0 likes
2 replies
martinbean's avatar
Level 80

@ignaciodev It’s not discouraged, it’s just not how middleware works.

You should see middleware as “layers” that a request is passing through. The request either passes through a middleware, or a response is returned. A middleware shouldn’t know about any other middleware (or if there even is any other middleware), and definitely shouldn’t be trying to call other middleware, otherwise you may end up in an infinite loop.

For your case, you’re just describing authorisation. So either do the check in your controller action:

public function show(Request $request, VideoCall $videoCall)
{
}

Or create a dedicated middleware class that does the logic of checking if either the authenticated user is the owner of the model, or if the request has a valid signature:

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use RuntimeException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class EnsureUserIsResourceOwnerOrRequestIsSigned
{
    public function handle(Request $request, Closure $next, string $parameter)
    {
        $model = $request->route($parameter);

        // Throw exception if requested route parameter is not a bound model
        throw_unless(
            condition: $model instanceof Model,
            exception: new RuntimeException(
                sprintf('Route parameter [%s] is not a model instance.', $parameter)
            ),
        );

        // First check if user is authenticated
        // and that model's user relation equals the authenticated user
        if (($user = $request->user()) && $model->user->is($user)) {
            return $next($request);
        }

        // Otherwise check the request is signed
        if ($request->hasValidSignature()) {
            return $next($request);
        }

         // If we are here, user is neither owner nor is request signed
        throw new AccessDeniedHttpException();
    }
}

You would use this middleware by adding it to routes, and specifying the name of the route parameter you want to check the user for.

So, given the route:

Route::get('/video-calls/{video_call}', [VideoCallController::class, 'show']);

You’d do something like:

$this->middleware('resource_owner:video_call');
1 like

Please or to participate in this conversation.