muazzambuilds/filament-ban
Filament Ban is a Filament plugin for managing bans directly from your admin panel, letting you block users or IPs, review active and expired bans, and lift restrictions without leaving the dashboard. Simple to install and configure for Laravel apps.
This is a small, single-purpose package (10 stars, MIT), so treat the README as the source of truth and read the source itself before adopting it — the surface area is tiny enough to audit in a sitting.
Minimal setup, in order:
composer require muazzambuilds/filament-ban
php artisan vendor:publish --tag="filament-ban-config" # check actual tag in README
php artisan vendor:publish --tag="filament-ban-migrations"
php artisan migrate
Then wire it into your panel. Filament v3/v4 plugins are registered on the panel, not globally:
// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(\MuazzamBuilds\FilamentBan\FilamentBanPlugin::make());
}
Add whatever trait the package ships (likely Bannable / HasBans) to your User model, and confirm the middleware alias is registered — the package's service provider should do this, but check bootstrap/app.php if you're on Laravel 11+ where middleware registration moved.
Where to look first: the migration (what columns: banned_at, expires_at, reason, banned_by?), then the middleware (what it blocks and what it returns), then the Filament resource/action (how bans are issued from the UI). Those three files tell you 90% of the behavior.
First use: ban yourself from a staging panel via the resource action, then hit the panel, an API route, and the login page in an incognito window. You'll immediately see which surfaces the middleware actually covers — the description promises panel, API, and login, but coverage depends on how you attach the middleware.
Attach the middleware where it actually matters. Don't assume installing the plugin covers your API. Typical pattern:
// routes/api.php
Route::middleware(['auth:sanctum', 'ban'])->group(function () {
// ...
});
For web/login, the package usually hooks the web group or the Authenticate middleware. Verify by unbanning/banning a test user against a raw Route::get('/dashboard').
Ban from the Filament resource, not tinker. The value of the package is the admin UX. Use the table action on the users resource so banned_by, reason, and timestamps are populated consistently. If you need to ban programmatically (e.g., after N failed logins), call the model method the package exposes rather than setting columns directly, so any events/cache invalidation fire.
Temporary suspensions. If the package supports expires_at, schedule the unban rather than relying on a middleware check-at-request-time — otherwise the user stays "banned" in the UI until they hit a route. A php artisan schedule:run job calling $user->unban() on expired rows keeps the admin table honest:
Schedule::command('model:prune')->daily(); // or a custom command iterating expired bans
Separate "banned" from "suspended" in your policies. A permanent ban and a 24-hour suspension often need different UX (403 page vs. "try again later"). If the package only gives you one middleware, wrap it or add a second middleware that reads the expiry and returns a tailored response.
Gate it behind a permission. Banning is destructive. Wrap the Filament action in ->visible(fn () => auth()->user()->can('ban-users')) and add the ability to a policy — don't let every panel user ban admins.
Notification on ban. Hook the model event (Banned / Unbanned, if the package fires them) to send a mail or Slack message. If no events exist, override the model method in a subclass or use an observer on updated when banned_at transitions from null.
Session and token invalidation is not automatic. A ban stops new requests through the middleware, but an already-authenticated session with cached Filament panel data may still render until the next request. For API, personal access tokens issued via Sanctum keep working unless the middleware checks on every request — verify it does, and consider $user->tokens()->delete() on ban.
Middleware ordering. If your ban middleware runs before auth, $request->user() is null and the check silently passes. Place it after the auth middleware in the group, or have it resolve the user defensively.
Filament's own auth pipeline. Filament panels have their own login flow; if the package only hooks Laravel's default LoginController, users can still log into the panel via /admin/login. Test both auth middleware and the panel login explicitly. On Filament v3+, you may need to add the check to canAccessPanel() on the User model as a belt-and-braces measure:
public function canAccessPanel(Panel $panel): bool
{
return ! $this->isBanned();
}
Published migrations vs. package migrations. If the migration isn't publishable, don't hack the vendor folder — add your own migration to alter the table. If it is publishable, publish it immediately so future package updates don't silently re-run column additions.
Low-maintenance risk. At 10 stars and a 0.21 assessment score, assume the package may go unmaintained. Pin the version in composer.json, read every file you depend on, and be prepared to fork. The logic is likely < 300 lines — a local app/Support/Ban.php trait is a viable escape hatch if the package breaks on a Filament major upgrade.
Testing. The middleware reads from the DB, so RefreshDatabase + a factory state is enough:
User::factory()->banned()->create();
$this->actingAs($user)->get('/admin')->assertForbidden();
Assert on the specific status the package returns (403 vs. redirect to a "you're banned" page) — this is the most common place behavior drifts between versions.
Extension points to check for: a configurable redirect/route for banned users, a custom ban model binding, and a shouldBan callback. If none exist, wrap the middleware in your own class that delegates and adds the customization — don't edit vendor code.
Logging. Log ban events with the acting admin's ID. When a customer disputes a ban, "who did it and why" matters more than the ban itself.
How can I help you explore Laravel packages today?