Filament - Comment forcer le renouvellement du mot de passe lors de la première connexion de l'utilisateur

Julien BOYER
Julien BOYER (aka. yebor974) Buy Me A Coffee
Publié le 03 juil. 2024
Cet article décrit comment nous pouvons obliger un nouvel utilisateur créé par un administrateur à renouveler son mot de passe lors de sa première connexion.

Cet article utilise le Plugin Filament Renew Password.

  1. Commencez par installer et enregistrer le plugin sur le panel.
composer require yebor974/filament-renew-password

Nous publions la migration par défaut du plugin qui ajoutera deux colonnes à la table des utilisateurs : last_password_renew_at et force_renew_password

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

Nous enregistrons le plugin sur le panel associé et ajoutons le processus de renouvellement forcé ainsi que la gestion de la date de dernier changement de mot de passe.

use Yebor974\Filament\RenewPassword\RenewPasswordPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(
            (new RenewPasswordPlugin())
                ->forceRenewPassword() // active le processus de renouvellement forcé
                ->timestampColumn() // active la colonne last_password_renew_at, en la mettant à jour à chaque modification de mot de passe.
        )
    );
}

Nous implémentons RenewPasswordContract dans le modèle User, ajoutons le trait RenewPassword par défaut et déclarons les attributs fillable.

Nous déclarerons simplement les attributs name et email pour l'utilisateur pour notre exemple.

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

Si nous définissons true pour l'attribut force_renew_password, l'utilisateur sera automatiquement redirigé vers le processus de renouvellement du mot de passe.

  1. Créer la ressource User
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(),
                ]),
            ]);
    }
    
    //...
}

Maintenant, nous devons générer un mot de passe par défaut et inviter l'utilisateur à se connecter et à renouveler son mot de passe. Sur la page CreateUser.php, nous redéfinissons les fonctions mutateFormDataBeforeCreate et handleRecordCreation comme suit :

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

    protected string $password;

    protected function mutateFormDataBeforeCreate(array $data): array
    {
        $this->password = Str::password(12); // génére un mot de passe par défaut de 12 caractères
        $data['password'] = bcrypt($this->password);
        $data['force_renew_password'] = true; // pour obliger l'utilisateur à renouveler le mot de passe lors de sa prochaine connexion

        return $data;
    }

    protected function handleRecordCreation(array $data): Model
    {
        /** @var User $user */
        $user = parent::handleRecordCreation($data); // gére la création du nouvel utilisateur

        $user->notify(new NewAccount($this->password)); // notifie le nouvel utilisateur avec les détails du compte

        return $user;
    }
}

La classe de notification NewAccount est la suivante :

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

C'est tout !

  1. Nous pouvons tester notre code
  • Créez un utilisateur

  • Recevez la notification (ici avec le serveur de messagerie smtp mailpit)

  • Connectez-vous et renouvelez le mot de passe

Autres articles

Plugins

Me contacter

Que vous ayez une idée à concrétiser, une problématique à résoudre ou un défi technique de grande envergure à relever, ne perdez plus de temps. Contactez-moi dès maintenant et laissez-moi vous aider à transformer vos ambitions en réalité. Mon expertise est à votre disposition pour concrétiser vos projets avec succès.

Situation géographique

Paris & Ile de la Réunion (France - UTC+4)