spatie / laravel-multitenancy

Make your Laravel app usable by multiple tenants
https://spatie.be/docs/laravel-multitenancy
MIT License
1.08k stars 154 forks source link

Laravel Must Verfiy Email with tenancy #249

Closed sys-auditing closed 3 years ago

sys-auditing commented 3 years ago

Hi, Any help would be really appreciated.

I'm using spatie/laravel-multitenancy . I'm trying to register the user account with tenancy in placed. The user does get registered & the email is sent to them. The email looks like the following.

http://example.com/email/verify/1?expires=1551615525&signature=b389f8c5d25c426c966ba0092ee062ab4e3febaebab5dcfd083549c7c2362150

When the user clicks on it, the account doesn't get activated.

The user will have to log in to there dashboard, and resend the verification email from http://abc.example.com, then the URL is generated correctly and the account is activated successfully.

http://abc.example.com/email/verify/1?expires=1551615525&signature=b389f8c5d25c426c966ba0092ee062ab4e3febaebab5dcfd083549c7c2362150

My actual question is can we modify or hook into URL::temporarySignedRoute? to add the sub-domain name inside the URL? so it should look like http://abc.example.com

BartMommens commented 3 years ago

@sys-auditing,

Are you using Laravel Fortify? If that is the case you can override the functionality of the link creation.

If you are using fortify:

Create New notification and extend fortify verifyemail notification and override the desired functions (Note i just altered it a bit to give idea code is not tested )

namespace App\Notifications;

use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;

class CustomUserEmailVerifyNotification extends VerifyEmail
{
    /**
     * Get the verification URL for the given notifiable.
     *
     * @param  mixed  $notifiable
     * @return string
     */
     protected function verificationUrl($notifiable)
    {
        if (static::$createUrlCallback) {
            return call_user_func(static::$createUrlCallback, $notifiable);
        }

        $email = $notifiable->getEmailForVerification();

        //Get the tenantdomain and set it
        $route_name = Tennant::current()->domain.'verifyemailaddress/';

        $id = $notifiable->getKey();
        $hash = sha1($notifiable->getEmailForVerification());

        $tempSignedUrl = URL::temporarySignedRoute(
            $route_name,
            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 1)),
            [
                'id' => $id,
                'hash' => $hash,
            ]
        );

        return $tempSignedUrl;

    }

    /**
     * Get the verify email notification mail message for the given URL.
     *
     * @param  string  $url
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    protected function buildMailMessage($url)
    {
        return (new MailMessage)
            ->subject(Lang::get('--> Verify Email Address'))
            ->line(Lang::get('Please click the button below to verify your email address.'))
            ->action(Lang::get('Verify Email Address'), $url)
            ->line(Lang::get('If you did not create an account, no further action is required.'));
    }
}

User model: (add custom notification)

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable, TwoFactorAuthenticatable, HasApiTokens;

    public function sendAPIEmailVerificationNotification()
    {
        $this->notify(new \App\Notifications\CustomUserEmailVerifyNotification());  //pass the currently logged in user to the notification class
    }
}

And in the controller where user registers: (CreateNewUser is also part of fortify)

  use App\Actions\Fortify\CreateNewUser;
  use App\Http\Controllers\Controller;
  use Illuminate\Auth\Events\Registered;
  use Illuminate\Http\Request;

class RegisterController extends Controller
{

  public function registerNewUser(Request $request, CreateNewUser $userCreator){
     $user  = $userCreator->create($request->all());
    $user->sendAPIEmailVerificationNotification();
    return response(['message' => 'ok'],204);
 }

}

This should work i think. But there is a lot of detailed doc on how to customize email verification with fortify.

Hope it helps you a bit further

BartMommens commented 3 years ago

here is where i looked Google results

sys-auditing commented 3 years ago

Thank you @BartMommens, but unfortunately i'm using a Laravel UI scaffolding.

Thanks.

BartMommens commented 3 years ago

Mmmmmm you should be able to use the same principals for it i guess. Create new notification and then create the signed url and set up the sending and validation part. Maybe take a look in the source code of Fortify, take what you need?

sys-auditing commented 3 years ago

ok, i'll check.

Thank you

masterix21 commented 3 years ago

http://example.com/email/verify/1?expires=1551615525&signature=b389f8c5d25c426c966ba0092ee062ab4e3febaebab5dcfd083549c7c2362150

Users are registered by tenant or landlord domain?

sys-auditing commented 3 years ago

User registred by tenant

James4645 commented 3 years ago

Did a little digging in laracel source. Looks like you can use /Illuminate/Auth/Notifications/VerifyEmail::createUrlUsing() to register a callback to your own function to generate the url that is added to the default notification email.

masterix21 commented 3 years ago

I'm closing here because it isn't an issue related to the package, but feel free to reply.