givebutter/laravel-keyable
Givebutter's Laravel Keyable brings simple, secure API key authentication to Laravel apps. Add the HasApiKeys trait to any model, generate and hash keys, protect routes with middleware, and manage multiple keys per user with straightforward revocation and expiration.
composer require givebutter/laravel-keyable
php artisan vendor:publish --tag="keyable-config"
php artisan vendor:publish --tag="keyable-migrations"
php artisan migrate
Keyable trait (and contract, if you want type-hinting) to any model that should be able to authenticate — usually App\Models\User.config/auth.php using the package's driver, pointing at your existing provider:
'guards' => [
'api' => ['driver' => 'keyable', 'provider' => 'users'],
],
$user->generateKey('cli') (or the package's keyable:generate command). Copy the plaintext value — this is the only time you'll see it.Route::middleware('auth:api')->get('/user', fn () => request()->user()); and send Authorization: Bearer <key>.First real use case: an internal/service-to-service API where you don't want a full OAuth/Sanctum token flow — generate one key per integration, name it, and revoke it independently.
Per-integration keys, not per-user keys. Call generateKey() with a descriptive name ('zapier', 'mobile-ios') so $user->keys() gives you a revocable inventory. Rotate by creating a new key, shipping it, then deleting the old one — no user-visible downtime.
Auth is just a guard, so compose normally. auth:api plays fine with policies, gates, $request->user(), and RateLimiter::for('api', fn ($r) => Limit::perMinute(60)->by($r->user()?->id ?: $r->ip())). Put auth:api before throttle:api in the middleware stack so throttling can key off the authenticated model.
Use the new KeyableAuthenticated event (v3.1.1) as your audit hook. It fires on successful key auth, which is the right seam for:
last_used_at / IP / user-agent on the ApiKey record,Event::listen(KeyableAuthenticated::class, function ($event) {
$event->key?->forceFill(['last_used_at' => now(), 'last_used_ip' => request()->ip()])->save();
});
Keep listeners ShouldQueue if they touch the network — this runs on every authenticated request.
Testing. Generate a key in a factory or test setup and hit routes with $this->withHeader('Authorization', "Bearer {$key}"). Assert 401 for a revoked/expired key by deleting or expiring the model before the request.
Provisioning. Wrap key creation in an artisan command or Filament/Nova action so keys are never generated by hand in production tinker; log the plaintext to a secure channel the user can read once.
?api_key=, it will end up in access logs, Referer headers, and error trackers. Restrict clients to the Authorization header, or strip the param at your edge proxy.keyable.php requires php artisan config:cache (or config:clear) — a stale cached config is the usual cause of "guard [api] is not defined."auth:sanctum and auth:api on separate route groups; combining them in one auth:sanctum,api middleware means either credential works, which is rarely what you intend.api_keys table with forever-valid keys is the classic failure mode.KeyableAuthenticated, so pair it with Laravel's Illuminate\Auth\Events\Failed or middleware-level logging if you need brute-force detection.Keyable trait's key-generation method (override in your model if you need a custom prefix or entropy source), and the event listener for post-auth side effects.How can I help you explore Laravel packages today?