Skip to content

Sanctum API token abilities are never enforced anywhere, despite UI and tests implying they are a security boundary #53

Description

@morcen

What

Jetstream's API-token feature (Features::api()) is now enabled (see Update below), but none of the API controllers check a token's abilities — auth:sanctum only verifies a token is valid, not what it's scoped to do. The frontend and test suite both reference an abilities/permissions concept that has no actual enforcement anywhere in the codebase.

Update (2026-09-08): Commit 6d2d0be (PR #218, "fix: enable Jetstream API feature so users can issue API tokens", fixing #93) uncommented Features::api() in config/jetstream.php, so /user/api-tokens is now reachable and users can actually create scoped personal access tokens through the UI. That PR only flips the feature flag and adds a regression test asserting the flag stays enabled (tests/Feature/ApiTokenFeatureEnabledTest.php) — it does not add any tokenCan()/ability: enforcement to the Api controllers. This means the risk described below is no longer hypothetical: users can now go to the UI, pick "read-only" or a limited ability set for a new token, and that token will silently grant full read/write/delete access anyway, because nothing checks abilities on any request. This is a live discrepancy between what the UI promises and what the API enforces, not just a landmine for a future re-enable.

Where

  • config/jetstream.php: Features::api() is enabled (as of 6d2d0be).
  • tests/Feature/ApiTokenPermissionsTest.php, CreateApiTokenTest.php, DeleteApiTokenTest.php are presumably un-skipped now that Features::hasApiFeatures() is true (re-verify — 6d2d0be didn't touch these files directly, so confirm they still pass with the feature on and aren't still gated some other way).
  • resources/js/Pages/API/Index.vue and its ApiTokenManager component present create/read/update/delete permission checkboxes to the user, implying tokens can be scoped.
  • None of app/Http/Controllers/Api/EndpointController.php, EventController.php, DeliveryController.php, or WebhookController.php call $request->user()->tokenCan(...) or apply an ability: middleware anywhere — auth:sanctum alone is still the only check on every route (verified: no tokenCan/ability: matches anywhere under app/Http/Controllers/Api/ or routes/api.php).

Why it matters

Now that Features::api() is on, this is an active gap, not a future trap: any user who creates a token via the UI and deliberately restricts its abilities (e.g. a "read-only" token handed to a third-party integration) is getting no actual restriction — that token can create/update/delete endpoints and events, trigger arbitrary webhook deliveries, and read all delivery data, regardless of what was selected.

Suggested fix

Either:

  • Add ability: middleware / explicit tokenCan() checks to every mutating action in the Api controllers, matching the abilities the frontend already offers, or
  • If API tokens aren't intended to be scoped, remove the misleading permission checkboxes from resources/js/Pages/API/Index.vue/ApiTokenManager and delete/adjust the ability-related test files, so nothing implies a security boundary that isn't implemented.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions