jeffbeltran/sanctum-tokens
A Laravel package that adds full personal access token management to Sanctum: create, list, and revoke tokens with abilities and expiration, plus ready-made controllers, routes, and helpers for SPA and mobile authentication.
What this is (and isn't): a Nova resource tool that renders a token panel for a tokenable model. It does not add token infrastructure — Sanctum already stores personal_access_tokens; this gives you a UI to list, create, and revoke them.
Minimal path:
composer require jeffbeltran/sanctum-tokens
php artisan nova:publish # rebuild Nova assets — required after install and after every upgrade
Prerequisites you almost certainly already have:
laravel/nova installed and licensed. Don't buy Nova just for this.laravel/sanctum installed with personal_access_tokens migrated.Laravel\Sanctum\HasApiTokens on the model whose detail page hosts the panel.Register it in a Resource's fields(), not in NovaServiceProvider::tools():
use JeffBeltran\SanctumTokens\SanctumTokens;
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
Section::make('API Access', [
SanctumTokens::make(),
]),
];
}
First useful case: open a User detail page in Nova → create a token with a name and abilities → copy the plaintext token immediately (shown once) → delete the row to revoke. That's the whole loop, and it's the one to demo to support/security before wiring anything else.
Where to look when something breaks: vendor/jeffbeltran/sanctum-tokens/src/ for the tool class and fluent API surface, resources/js/ for the Vue component, and the parent model's personalAccessTokens relation. The package is thin — nearly every failure is Sanctum config or a missing trait.
One tool instance per tokenable model. Tokens are scoped by tokenable_type, so the panel shows the parent resource's tokens only. Users and Admins means two resources and two SanctumTokens::make() calls. If tokens belong to a Team, attach the tool to the Team resource, not the User resource — cross-model views need custom code.
Constrain abilities server-side rather than free text. Sanctum abilities are plain strings enforced with $token->can('orders:read'); letting operators type arbitrary scopes guarantees drift between issuance and enforcement. Pass a fixed list from the tool's fluent API (verify the exact method name — abilities() vs scopes() vs a config array — against the tool source, since docs are thin) and mirror that list in your OpenAPI spec.
Gate visibility at the field level, not just in the policy. The package ships no token policy, so anyone with resource update can mint or revoke that model's API credentials — effectively impersonation via API. Add a dedicated policy method and conditionally return the tool:
$request->user()->can('manageApiTokens', $this->resource)
? SanctumTokens::make()
: null,
Let the tool own per-token operations; use Nova Actions for bulk. "Revoke all tokens for this user," "rotate for the whole tenant," and "clean up tokens unused for 90 days" are Actions that delete from personal_access_tokens and log. Mixing bulk behavior into the panel confuses operators and makes the blast radius unclear.
Audit at the Sanctum model, not the tool. Hooking PersonalAccessToken gives coverage for revocations from the Nova UI and from your API in one place:
PersonalAccessToken::deleted(
fn ($token) => activity()->on($token)->log('token revoked')
);
Prefer subclassing over forking for extra metadata (environment, owner, ticket reference): register a PersonalAccessToken subclass via Sanctum::usePersonalAccessTokenModel().
Normalize token names on creation (observer on the token model) if your convention is <user-id>-<environment>. It makes last_used_at triage across dozens of rows tractable, and it's the difference between a usable panel and a wall of "my-token" rows.
NovaServiceProvider::tools() or on a dashboard it silently renders nothing. It must be in a Resource's fields().HasApiTokens means no relation; unmigrated Sanctum means a query error, not a friendly empty state. Check both before reading package source.['*'] is a wildcard in Sanctum's ability check. If you expose an "all abilities" option, style it distinctly — a stray click there is privilege escalation.config/sanctum.php's expiration, but there's no "expires at" column by default. Note it in the section heading or extend the view.last_used_at is lazy/throttled. "Never used" may just mean "not used since deploy." Don't build a stale-token dashboard on it without understanding Sanctum's update cadence.v2.1.1 (March 2024) — a CSS-only fix aligning with default Nova styles, and it predates Nova 5. If spacing/borders/typography look off, confirm you're on >=2.1.1 and re-ran php artisan nova:publish before suspecting incompatibility. Test any Nova 5 upgrade in a branch.HasApiTokens on the model, (2) migrations run, (3) tool in fields(), (4) nova:publish after install and after upgrade, (5) browser console for failing nova-api calls to the tool endpoint.vendor:publish (config/views), subclassing the tool in-app, and swapping the PersonalAccessToken model. If you override the Vue view, re-check your CSS against v2.1.1's Nova-aligned styles so customizations don't drift.How can I help you explore Laravel packages today?