Filament - New User Verification by Admin

2025-05-23

In your Filament application, you might want multiple user verifications by email and admin after a user registers. Let's see how to do that.

Imagine we have two Filament panels in this tutorial: admin and owner.


Email Verification

Email verification can be enabled simply by adding the MustVerifyEmail interface to the user User Model and enabling verification in the panel provider.

app/Models/User.php:

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Foundation\Auth\User as Authenticatable;
 
class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{
// ...
}

app/Providers/Filament/OwnerPanelProvider.php:

use Filament\Panel;
 
public function panel(Panel $panel): Panel
{
return $panel
// ...
->login()
->registration()
->emailVerification()
// ...
}

For email verification, that is enough; filament will handle everything.


Manual Verification by Admin

Now, let's add manual verification by an admin user. First, we need a column in the users table similar to how the email_verified_at column works for email verification.

database/migrations/xxxx_add_owner_verified_at_to_users_table.php:

Schema::table('users', function (Blueprint $table) {
$table->timestamp('owner_verified_at')->nullable();
});

app/Models/User.php:

class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{
// ...
 
protected $fillable = [
'name',
'email',
'password',
'owner_verified_at',
];
 
// ...
 
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'owner_verified_at' => 'datetime',
];
}
 
// ...
}

Next, we need Middleware to check if the user is verified; if not, we need to restrict access. This Middleware must be added to the authMiddleware list in the panel provider.

php artisan make:middleware OwnerUserVerifiedMiddleware

app/Middleware/OwnerUserVerifiedMiddleware.php:

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class OwnerUserVerifiedMiddleware
{
public function handle(Request $request, Closure $next)
{
if (is_null($request->user()->owner_verified_at) {
abort(Response::HTTP_FORBIDDEN, 'Your account must be verified by an admin.');
}
 
return $next($request);
}
}

In the panel provider:

app/Providers/Filament/OwnerPanelProvider.php:

use App\Http\Middleware\OwnerUserVerifiedMiddleware;
 
public function panel(Panel $panel): Panel
{
return $panel
// ...
->authMiddleware([
Authenticate::class,
OwnerUserVerifiedMiddleware::class,
]);
}

It would be great if after registration admin users would get notified about a new user that needs verification. There are multiple ways to do that. We will use a custom registration page.

php artisan make:filament-page Register

You should choose the panel to which the page will be added. In my case, the panel will be Owner, which corresponds to the namespace.

app/Filament/Owner/Pages/Register.php:

use App\Models\User;
use Filament\Pages\Auth\Register as BaseRegister;
use Illuminate\Database\Eloquent\Model;
use App\Notifications\OwnerUserCreatedNotification;
 
class Register extends BaseRegister
{
protected function handleRegistration(array $data): Model
{
/** @var User $user */
$user = parent::handleRegistration($data);
 
$admins = User::whereRelation('role', 'name', 'admin')->get();
 
$admins->each->notify(new OwnerUserCreatedNotification($user->id));
 
return $user;
}
}

Here, we get all the users with the role admin and send the OwnerUserCreatedNotification notification to each one.

app/Notifications/OwnerUserCreatedNotification.php:

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Filament\Admin\Resources\UserResource;
use Illuminate\Notifications\Messages\MailMessage;
 
class OwnerUserCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;
 
public function __construct(private readonly int $userId) {}
 
public function via(User $notifiable): array
{
return ['mail'];
}
 
public function toMail(User $notifiable): MailMessage
{
return (new MailMessage)
->line('A new owner user has been created.')
->line('Please login to the admin panel and verify the user.')
->action('Visit Admin Panel', UserResource::getUrl());
}
}

You would have a resource for managing users in the Admin panel, typically a UserResource. Let's add a verify action to the table.

app/Filament/Admin/Resources/UserResource.php:

use Filament\Notifications\Notification;
use App\Notifications\OwnerUserVerifiedNotification;
 
public static function table(Table $table): Table
{
return $table
// ...
->actions([
Tables\Actions\Action::make('verify')
->icon('heroicon-o-check-circle')
->visible(fn (User $user) => is_null($user->owner_verified_at))
->action(function (User $user) {
if (is_null($user->owner_verified_at)) {
return;
}
 
$user->touch('owner_verified_at');
 
$user->notify(new OwnerUserVerifiedNotification());
 
Notification::make()
->success()
->title("User {$user->name} verified")
->send();
}),
Tables\Actions\ViewAction::make(),
])
// ...
}

Here, we show action only if the owner_verified_at column is null. After hitting this action, the owner_verified_at column is updated, and a notification is sent to the user to inform him that he was approved by an admin. To the admin user, a success notification is shown.

app/Notification/OwnerUserVerifiedNotification.php:

use App\Models\User;
use Illuminate\Bus\Queueable;
use Filament\Pages\Dashboard;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
 
class OwnerUserVerifiedNotification extends Notification implements ShouldQueue
{
use Queueable;
 
public function __construct() {}
 
public function via(User $notifiable): array
{
return ['mail'];
}
 
public function toMail(User $notifiable): MailMessage
{
return (new MailMessage)
->line('An admin has verified your account.')
->action('Visit Owner Panel', Dashboard::getUrl(panel: 'owner'));
}
}

Now, when the user has both verifications, he can access the panel.

A few of our Premium Examples: