ยท yebor974 ยท Getting Started, Tutorials, Plugins & Extensions

Enforcing password updates for admin-created user accounts

Learn how to set up mandatory password renewal for user accounts created by administrators, ensuring better security and user onboarding through Filament PHP.

Enforcing password updates for admin-created user accounts - picture

Managing user accounts in web applications often involves administrators creating accounts for users. In such cases, ensuring account security is paramount. One effective measure is to force users to reset their password upon their first login. This practice not only reinforces security but also allows users to set a password they prefer.

This article uses the Filament Renew Password Plugin.

Step 1: Install and register the plugin on panel

composer require yebor974/filament-renew-password

Publish default plugin migration that will add two columns to users table : last_password_renew_at and force_renew_password

php artisan vendor:publish --tag="filament-renew-password-migrations"
php artisan migrate

Register the plugin to panel and add force renewal process and timestamp management.

use Yebor974\Filament\RenewPassword\RenewPasswordPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(
            (new RenewPasswordPlugin())
                ->forceRenewPassword() // activate the force renewal process
                ->timestampColumn() // activate last_password_renew_at column, updating it with each password renewal.
        )
    );
}

Step 2: Setup User Model

Implement RenewPasswordContract on User Model, add default RenewPassword trait and declare fillable attributes.

For example, we just declare a name and email attributes for user.

use Illuminate\Foundation\Auth\User as Authenticatable;
use Yebor974\Filament\RenewPassword\Contracts\RenewPasswordContract;
use Yebor974\Filament\RenewPassword\Traits\RenewPassword;
//...

class User extends Authenticatable implements RenewPasswordContract
{
    use RenewPassword;
	
    protected $fillable = [
        'name',
        'email',
        'force_renew_password'
    ];
	
    //...
}

If force_renew_password attribute is set to true, the user will be automatically redirect to the renewal password process.

Step 3: Create UserResource

php artisan make:filament-resource User
class UserResource extends Resource
{
    protected static ?string $model = User::class;

    protected static ?string $navigationIcon = 'heroicon-o-users';

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\Section::make()
                    ->schema([
                        TextInput::make('name')
                            ->required(),
                        TextInput::make('email')
                            ->required()
                            ->unique(ignoreRecord: true)
                    ])->columns(2)
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('name')
                    ->searchable(),
                Tables\Columns\TextColumn::make('email')
                    ->searchable(),
                Tables\Columns\IconColumn::make('force_renew_password')
                    ->boolean()
            ])
            ->filters([
                //
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\BulkActionGroup::make([
                    Tables\Actions\DeleteBulkAction::make(),
                ]),
            ]);
    }
	
		//...
}

Step 4: Generate a default password and send it with email notification

Now, need to generate default password and invite user to connect and renew password. On CreateUser.php page we have to override the mutateFormDataBeforeCreate and handleRecordCreation functions like that:

class CreateUser extends CreateRecord
{
    protected static string $resource = UserResource::class;

    protected string $password;

    protected function mutateFormDataBeforeCreate(array $data): array
    {
        $this->password = Str::password(12); // generate a default password with length of 12 caracters
        $data['password'] = bcrypt($this->password);
        $data['force_renew_password'] = true; // to force user to renew password on next login

        return $data;
    }

    protected function handleRecordCreation(array $data): Model
    {
        /** @var User $user */
        $user = parent::handleRecordCreation($data); // handle the creation of the new user

        $user->notify(new NewAccount($this->password)); // notify the new user with account details

        return $user;
    }
}

The NewAccount notification class is:

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\HtmlString;

class NewAccount extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     */
    public function __construct(protected string $password, protected ?Model $tenant = null)
    {
        $this->afterCommit();
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail(object $notifiable): MailMessage
    {
        $appName = config('app.name');

        return (new MailMessage)
            ->subject("Your account has been created on $appName")
            ->line("Here are your login details:")
            ->line(new HtmlString("<strong>Email</strong> : {$notifiable->email}"))
            ->line(new HtmlString("<strong>Temporary password</strong> : {$this->password}"))
            ->line("You will be prompted to change this temporary password at your next login.")
            ->action('Go to app', filament()->getUrl($this->tenant));
    }

    /**
     * Get the array representation of the notification.
     *
     * @return array<string, mixed>
     */
    public function toArray(object $notifiable): array
    {
        return [
            //
        ];
    }
}

That's all!

Screenshots

  • Create user

  • Receive notification (with smtp mailpit mailer)

  • Login and renew password

Avatar of yebor974
yebor974
Freelance - French IT Engineer passionate about Laravel and Filament PHP, creating innovative solutions to simplify web development.
React
Share post

Stay ahead in the Filament Mastery adventure

Join our community of Filament PHP enthusiasts! Get exclusive tips, tutorials, and updates delivered straight to your inbox.