Laravel Filament MD5 Password Authentication Guide

Learn how to enable MD5 password authentication in Laravel Filament by creating a custom hasher and updating the AuthServiceProvider for legacy systems.

⚠️ Security Warning:

MD5 is not secure for password hashing and should only be used for compatibility with legacy systems. Consider migrating to bcrypt or argon2 for secure password storage.

If you are working on a Laravel application using Filament Admin and you already have users table with passwords are stored as MD5 hashes, Laravel makes it possible to register a custom hash driver so authentication still works.

In this article, we will learn how to develop custom hash driver for legacy systems, which can use Filament Admin.

Step 1 – Create the MD5 Hasher Class

Laravel’s hash drivers only need three methods: make, check, and needsRehash. We’ll create a simple MD5-based hasher class as follows:

namespace App\Hashing;

class Md5Hasher
{
    public function make($value, array $options = [])
    {
        return md5($value);
    }

    public function check($value, $hashedValue, array $options = [])
    {
        return md5($value) === $hashedValue;
    }

    public function needsRehash($hashedValue, array $options = [])
    {
        return false;
    }
}

In this hasher class, we used md5 function to encrypt the data.

Step 2 – Register the MD5 Driver in AuthServiceProvider

We need to register this hash driver class to the app/Providers/AuthServiceProvider file as follows,

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Hash;
use App\Hashing\Md5Hasher;

class AuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app->make('hash')->extend('md5', function() {
            return new Md5Hasher;
        });
    }
}

Step 3 – Set Laravel to Use MD5 by Default

After register to the service provider, we need to change the authentication driver for laravel. You can do it from .env file or config/hashing.php file.

In .env:

HASH_DRIVER=md5

Or in config/hashing.php:

'default' => env('HASH_DRIVER', 'md5'),

Step 4 – Ensure Passwords Are Stored as MD5

When creating or updating users, Laravel will now use MD5 automatically:

use Illuminate\Support\Facades\Hash;
use App\Models\User;

$user = new User();
$user->name = 'Admin';
$user->email = 'admin@example.com';
$user->password = Hash::make('secret'); // stored as MD5
$user->save();

How It Works in Filament

Filament uses Laravel’s built-in authentication (Auth::attempt()), which in turn uses Hash::check(). Because we overrode the hash driver, Filament logins will automatically work with MD5 passwords.

Bonus: Supporting Both MD5 and bcrypt

If you’re migrating from MD5 to bcrypt, you can check both formats:

public function check($value, $hashedValue, array $options = [])
{
    if (md5($value) === $hashedValue) {
        return true; // MD5 match
    }

    return password_verify($value, $hashedValue); // bcrypt/argon
}

This way, old MD5 passwords still work, but you can rehash them to bcrypt on the next login.

Final Thoughts

  • Use MD5 only for compatibility with old systems.
  • If possible, rehash MD5 passwords to bcrypt or argon2 after the first successful login.
  • Filament will automatically use your custom MD5 logic since it relies on Laravel’s authentication system.

How to Insert Repeater Field Entries as Rows to Table in Laravel Filament

Learn how to convert repeater field JSON data into individual table rows in Laravel Filament using a custom button action. A practical guide for syncing structured form data.

When building admin panels using Laravel Filament, the Repeater field is a powerful way to collect dynamic sets of data — such as specifications, tags, or features. Often, these repeater entries are stored as a JSON array in the database. But what if you want to convert those JSON entries into individual rows in another table — for analytics, reporting, or normalization?

In this article, we’ll walk through how to:

  • Collect data using a Repeater field (stored as JSON)
  • Add a button to your Filament admin to insert each entry as a row in another table
  • Do this on-demand, without preloading data from the related table

The Use Case

Let’s assume you’re managing products with technical specifications.

  • You store specifications in a specifications JSON column of the products table using a Filament Repeater.
  • When a button is clicked (e.g., “Insert Repeater Entries”), each specification should be copied into a product_specifications table, with one row per entry.

Step-by-Step Guide

Step 1: Set Up the Repeater Field

Add a specifictions repeater field with multiple fields in product form as follows:

Repeater::make('specifications')
    ->schema([
        TextInput::make('key'),
        TextInput::make('value'),
        TextInput::make('unit'),
        TextInput::make('description'),
        TextInput::make('notes'),
    ])

This will create specifications repeater field in which you can add multiple rows of specifications for any product. This data is stored in the product_pecifications column of your products table as a JSON array.

Step 2: Create the Target Table

Create a migration for new table to hold individual specification entries using this command:

php artisan make:model ProductSpecification -m

It will create 2 files as follows:

  • Model File: app/Models/ProductSpecification.php
  • Migration File: database/migrations/xxxx_xx_xx_xxxxxx_create_product_specifications_table.php

Step 3: Run the Migration File

Update the migration file as per your repeater field entry as follows:

Schema::create('product_specifications', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->string('key')->nullable();
    $table->string('value')->nullable();
    $table->string('unit')->nullable();
    $table->text('description')->nullable();
    $table->text('notes')->nullable();
    $table->timestamps();
});

You can now run the migration using migrate command.

php artisan migrate

It will create a product_specifictions table in the database.

Step 4: Add a Button to Trigger the Insert

In your ProductResource\Pages\ViewProduct or EditProduct, add a custom action to generate specifications entries from the repeater field in product form.

use Filament\Actions\Action;

public function getHeaderActions(): array
{
    return [
        Action::make('Insert Repeater Entries')
            ->requiresConfirmation()
            ->action(function () {
                $specs = $this->record->specifications ?? [];

                if (!is_array($specs)) {
                    $specs = json_decode($specs, true) ?? [];
                }

                foreach ($specs as $spec) {
                    \App\Models\ProductSpecification::create([
                        'product_id'  => $this->record->id,
                        'key'         => $spec['key'] ?? null,
                        'value'       => $spec['value'] ?? null,
                        'unit'        => $spec['unit'] ?? null,
                        'description' => $spec['description'] ?? null,
                        'notes'       => $spec['notes'] ?? null,
                    ]);
                }

                $this->notify('success', 'Specifications inserted successfully.');
            }),
    ];
}

You can name the button anything, such as “Sync Specifications” or “Publish to Table”. It will manually extract the JSON data and insert it as rows in the product_specifications table.

Benefits of This Approach

  • Keeps your form simple and user friendly by storing repeater data in JSON.
  • Normalizes data later when needed — perfect for one-time inserts or batch operations.
  • Doesn’t require eager loading or nested relationship editing.

Avoid Duplicate Inserts

You can prevent duplicate imports by checking if rows already exists by adding the following check before adding the specifications in above code:

if ($this->record->productSpecifications()->exists()) {
    $this->notify('warning', 'Specifications already exist.');
    return;
}

Conclusion

Using repeater field gives you flexibility in how you manage structured, dynamic data in Laravel Filament. You can let users manage repeater fields easily, while keeping your database clean and relational by syncing data to separate tables on demand.

Whether for analytics, reporting, or integration, separating repeater entries into rows gives you the best of both worlds: JSON-based forms with relational data power.