Ben
Ben

Reputation: 401

How do I make the reset password url dynamic?

<?php

namespace Illuminate\Auth\Notifications;

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;

class ResetPassword extends Notification
{
    /**
     * The password reset token.
     *
     * @var string
     */
    public $token;

    /**
     * The callback that should be used to create the reset password URL.
     *
     * @var \Closure|null
     */
    public static $createUrlCallback;

    /**
     * The callback that should be used to build the mail message.
     *
     * @var \Closure|null
     */
    public static $toMailCallback;

    /**
     * Create a notification instance.
     *
     * @param  string  $token
     * @return void
     */
    public function __construct($token)
    {
        $this->token = $token;
    }

    /**
     * Get the notification's channels.
     *
     * @param  mixed  $notifiable
     * @return array|string
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Build the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable, $this->token);
        }

        if (static::$createUrlCallback) {
            $url = call_user_func(static::$createUrlCallback, $notifiable, $this->token);
        } else {
            $url = url(route('password.reset', [
                'token' => $this->token,
                'email' => $notifiable->getEmailForPasswordReset(),
            ], false));
        }

        return (new MailMessage)
            ->subject(Lang::get('Reset Password Notification'))
            ->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
            ->action(Lang::get('Reset Password'), $url)
            ->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
            ->line(Lang::get('If you did not request a password reset, no further action is required.'));
    }

    /**
     * Set a callback that should be used when creating the reset password button URL.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function createUrlUsing($callback)
    {
        static::$createUrlCallback = $callback;
    }

    /**
     * Set a callback that should be used when building the notification mail message.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function toMailUsing($callback)
    {
        static::$toMailCallback = $callback;
    }
}

Hi, I am using Laravel 7.6.2.

I keep on getting an error. I am trying to make a multiauth login system, and I am testing the password reset routes. The problem is that when I access the admin forgot password page, the email that is sent actually contains a link to the user password reset page, not the admin password reset page. So route('password.reset' should actually be route('admin.password.reset' for the admin request. But I really have no clue how to make this URL dynamic.... Help please!!

Upvotes: 10

Views: 6730

Answers (7)

Evol Rof
Evol Rof

Reputation: 2802

as @Bruno said

If you try calling ResetPassword::createUrlUsing then ResetPassword::toMailUsing in authserviceprovider boot that doesnt work

because in ResetPassword

public function toMail($notifiable)
   {
       if (static::$toMailCallback) {
           return call_user_func(static::$toMailCallback, $notifiable, $this->token);
       }

try this

ResetPassword::toMailUsing(function (User $notifiable, string $token) { $mail = config('mail.service');

        $url = sprintf('%s/reset/%s?email=%s', config('website'), $token, $notifiable->getEmailForPasswordReset());
        return (new MailMessage)
            ->subject('Reset Password Notification')
            
            ->action('Reset', $url)
            ;
    });

        return $this->buildMailMessage($this->resetUrl($notifiable));
    }

Upvotes: 0

Bruno
Bruno

Reputation: 162

Just a notice to anyone facing the following issue:

If you are trying to change the reset password URL link by using ResetPassword::createUrlUsing and it does not work, check if you are not already customizing the email using the ResetPassword::toMailUsing.

If you are doing something like:

ResetPassword::toMailUsing(function ($notifiable, $token) {
    return (new MailMessage)
        ->subject('Subject')
        ->action('btnActionText', 'https://link.com') // THIS MAY BE THE PROBLEM, You should customize here
        ->salutation('Thank you');
});

and doing the following, its not going to overwrite it:

ResetPassword::createUrlUsing(function (User $user, string $token) {
    return 'https://google.com.br';
});

The link in the button will be https://link.com, so be careful with this, I just spent 1 hour trying to figure out what it was.

Upvotes: 0

Hamodea Net
Hamodea Net

Reputation: 125

Here what I did in the User model

use Illuminate\Auth\Notifications\ResetPassword;


/**
 * Override the mail body for reset password notification mail.
 */
public function sendPasswordResetNotification($token)
{
    ResetPassword::createUrlUsing(function ($user, string $token) {
        return 'https://example.com/reset-password?token='.$token;
    });
    $this->notify(new ResetPassword($token));
}

Upvotes: 1

Juukie14
Juukie14

Reputation: 318

ResetPassword::createUrlUsing(function ($notifiable, $token) {
    $route = Request::is('admin/password/reset') 
        ? 'admin.password.reset'
        : 'password.reset';

    return url(route($route, [
        'token' => $token,
        'email' => $notifiable->getEmailForPasswordReset(),
    ], false));
});`

Upvotes: 0

Joren
Joren

Reputation: 951

Another option is to add this to the boot method in your AppServiceProvider:

ResetPassword::createUrlUsing(function ($notifiable, $token) {
    return "http://www.my-spa.co/password/reset/{$token}";
});

I use Laravel as an API and needed this to generate a link to my single page application url.

Upvotes: 17

bobylito
bobylito

Reputation: 3392

The ResetPassword notification provided by the Laravel framework allows custom URLs out of the box. The method createUrlUsing lets you provide a function that will generate the URL in the output email.

Example in the User model class:

// Import the ResetPassword class from the framework
use Illuminate\Auth\Notifications\ResetPassword;

class User extends Authenticatable {
    // ... the rest of your implementation

    // The customization of the email happens here
    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token) {
        // The trick is first to instantiate the notification itself
        $notification = new ResetPassword($token);
        // Then use the createUrlUsing method
        $notification->createUrlUsing(function ($token) {
            return 'http://acustomurl.lol';
        });
        // Then you pass the notification
        $this->notify($notification);
    }
}

I don't know if it's completely off topic but that was what I was looking for 😅

Upvotes: 7

AlexIL
AlexIL

Reputation: 573

I have done as following:

In Admin user class override sendPasswordResetNotification method:

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new AdminMailResetPasswordToken($token));
    }

In AdminMailResetPasswordToken extends default Laravel ResetPassword notification class:

namespace App\Notifications\Admin\Auth;

use Illuminate\Auth\Notifications\ResetPassword;

class AdminMailResetPasswordToken extends ResetPassword
{
    public static $createUrlCallback = [self::class, 'createActionUrl'];

    public static function createActionUrl($notifiable, $token)
    {
        return url(route('admins.password.reset', [
            'token' => $token,
            'email' => $notifiable->getEmailForPasswordReset(),
        ], false));
    }
}

Upvotes: 5

Related Questions