Skip to content

PASSAGE_ENABLED=false crashes registered routes instead of disabling them gracefully #97

Description

@morcen

Problem

config/passage.php documents the enabled switch as a safe way to disable Passage "without the need to remove route definitions", specifically so you can rule out Passage while troubleshooting route-related issues (README: "Set PASSAGE_ENABLED=false in your .env to disable all Passage proxying without removing route definitions").

In practice, Passage::get/post/...() registers routes unconditionally (src/Passage.php) — the enabled flag is never consulted at route-registration time. The only place it's checked is PassageServiceProvider::packageBooted(), which conditionally skips binding PassageServiceInterface when disabled:

if (isset($passage['enabled']) && $passage['enabled']) {
    $this->app->bind(PassageServiceInterface::class, PassageService::class);
}

PassageController's constructor requires PassageServiceInterface via constructor injection. If a request hits an already-registered Passage route while PASSAGE_ENABLED=false, the container cannot resolve the unbound interface and throws an unhandled Illuminate\Contracts\Container\BindingResolutionException ("Target class ... does not exist" / "is not instantiable"), which surfaces as a generic 500/Whoops error instead of the documented graceful disable.

Location

  • src/PassageServiceProvider.php:33-39 (packageBooted())
  • src/Http/Controllers/PassageController.php:37-44 (constructor requires PassageServiceInterface)
  • src/Passage.php:41-46 (register() never checks passage.enabled)

No test exercises hitting a Passage route with passage.enabled set to falsetests/Unit/PassageServiceProviderTest.php only asserts the binding exists when enabled.

Why it matters

This inverts the documented purpose of the switch: it's meant to let operators safely no-op Passage in production without editing route files, but currently doing so turns any hit on a Passage route into an unhandled exception, which is worse for troubleshooting than leaving Passage enabled.

Suggested fix

Have PassageController::handle() (or a thin guard before it) check config('passage.enabled', true) and return a clean response (e.g. 404 or a maintenance-style JSON error) when disabled, instead of relying on container resolution to fail. Add a regression test that hits a registered route with passage.enabled => false and asserts a graceful response.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions