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.
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:sanctumonly 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) uncommentedFeatures::api()inconfig/jetstream.php, so/user/api-tokensis 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 anytokenCan()/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 of6d2d0be).tests/Feature/ApiTokenPermissionsTest.php,CreateApiTokenTest.php,DeleteApiTokenTest.phpare presumably un-skipped now thatFeatures::hasApiFeatures()is true (re-verify —6d2d0bedidn'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.vueand itsApiTokenManagercomponent presentcreate/read/update/deletepermission checkboxes to the user, implying tokens can be scoped.app/Http/Controllers/Api/EndpointController.php,EventController.php,DeliveryController.php, orWebhookController.phpcall$request->user()->tokenCan(...)or apply anability:middleware anywhere —auth:sanctumalone is still the only check on every route (verified: notokenCan/ability:matches anywhere underapp/Http/Controllers/Api/orroutes/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:
ability:middleware / explicittokenCan()checks to every mutating action in the Api controllers, matching the abilities the frontend already offers, orresources/js/Pages/API/Index.vue/ApiTokenManagerand delete/adjust the ability-related test files, so nothing implies a security boundary that isn't implemented.