DEV Community

Cover image for Enforcing password updates for admin-created user accounts
yebor974 for Filament Mastery

Posted on • Originally published at filamentmastery.com on

Enforcing password updates for admin-created user accounts

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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.
        )
    );
}

Enter fullscreen mode Exit fullscreen mode

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'
    ];

    //...
}

Enter fullscreen mode Exit fullscreen mode

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(),
                ]),
            ]);
    }

        //...
}

Enter fullscreen mode Exit fullscreen mode

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;
    }
}

Enter fullscreen mode Exit fullscreen mode

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 [
            //
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

That's all!

Screenshots

  • Create user

  • Receive notification (with smtp mailpit mailer)

  • Login and renew password

📬 Join the community on filamentmastery.com — it's free!

Top comments (0)