phplaraveltokenemail-verification

laravel 8 : send email verification after registration laravel


I'm building a laravel API. I want when I register, email verify will be sent activation code to user email automatically.

The problem is when I create a new activation code, I create a new record in tokens table too, this record has user_id field, so for store it, I use JWTAuth::user()->id but I have this error:

Trying to get property 'id' of non-object

I know why this happens, because I did not enter any tokens and I don't know how handle it and where to create a new record in tokens table.

For more details I have:

AuthController : Login and register

 public function register(Request $request) {

        $validator = Validator::make($request->all(), [
            'name'=>'required|string|min:3|max:30',
            'email' => 'required|string|email|max:100|unique:users',
            'password' => 'required|string|confirmed|min:6',
        ]);

        if($validator->fails()){
            return response()->json($validator->errors()->toJson(), 400);
        }

        $user = User::create(array_merge(
                    $validator->validated(),
                    ['password' => bcrypt($request->password)],
                ));

        $token = JWTAuth::fromUser($user);

        dd($this->sendNotification());

        $user->$this->sendNotification();

        return response()->json([
            'message' => 'successfully created',
            'user' => $user,
            'token' => $token,
        ], 201);
    }

EmailVerifyController : for verify user email and validate activation code

public function emailVerify(Request $request){
        $data = $request->validate([
            'code' => 'required|size:10|numeric',
        ]);
        $interedCode = (int)$data['code'];//convert code from string to integer

        $userCode = Token::where('user_id' , JWTAuth::user()->id)->first();//find user from tokens table
        $activationCode = $userCode->code; //get activation code of user in tokens table
        $expires_in = (int)$userCode->expires_in; //get expire time of code

        $now = Carbon::now()->timestamp;


        if($interedCode == $activationCode) {
            if ($now < $expires_in) {
                    $user = JWTAuth::user()->id;
                    $findUser = User::find($user);
                    $findUser->email_verified_at = Carbon::now()->timestamp;
                    $findUser->save();

                    $token = Token::where('user_id', JWTAuth::user()->id)->first();
                    $token->status = 1;
                    $token->save();

                    return response()->json('email verified successfully', 200);
            } else {
                return response()->json('code expired', 400);
            }
        }else{
            return response()->json('wrong activation code' , 400);
        }
    }

SendNotificationTrait : for send email and create a new record in token table

trait EmailVerifyTrait
{
    public function sendNotification(){

        $random = $this->generateVerificationCode(6);

        $details = [
            'title' => 'Mail from ItSolutionStuff.com',
            'body' =>$random,
        ];

        Mail::to('*****@gmail.com')->send(new VerifyMail($details));

        return response()->json([
            'message'=>'your email verification code sent to your email'
        ] , 201);
    }

    public function generateVerificationCode($length = 6) {
        $characters = '0123456789';
        $charactersLength = strlen($characters);
        $code = '';
        for ($i = 0; $i < $length; $i++) {
            $code .= $characters[rand(0, $charactersLength - 1)];
        }

            $token = new Token();
            $token->user_id = JWTAuth::user()->id;
            $token->code = $code;
            $token->status = 0;
            $token->save();
   
        return $code;
}

tokens tables : has this fields : user_id , code , created_at , expires_in

So how can I handle creating new Token record in tokens table or should I use event listener?


Solution

  • for handle this email verification , i used laravel Notification : https://laravel.com/docs/8.x/notifications

    and i used $user->id for getting id and use it for user_id field in tokens table.

    codes:

    Register method

       use ActivationCode;
    
      public function register(Request $request) {
    
            $validator = Validator::make($request->all(), [
                'name'=>'required|string|min:3|max:30',
                'email' => 'required|string|email|max:100|unique:users',
                'password' => 'required|string|confirmed|min:6',
            ]);
    
            if($validator->fails()){
                return response()->json($validator->errors()->toJson(), 400);
            }
    
            //create user
            $user = User::create(array_merge(
                        $validator->validated(),
                        ['password' => bcrypt($request->password)],
                    ));
            //create token
            $token = JWTAuth::fromUser($user);
            
            //create a new activation code
            $activationCode = $this->generateVerificationCode();
          
            //create a new token 
            $newToken = new Token;
            $newToken->code = $activationCode;
            $newToken->status = 0;
            $newToken->user_id = $user->id;
            $newToken->save();
        
            //email details
            $details = [
                'greeting' => 'Hi'.$request->name,
                'body' => 'use this activation code for verify your email address',
                'activation_code'=>$newToken->code,
                'thanks' => 'thank you',
                'order_id' => 101
            ];
    
            //send email verify to user email
            Notification::send($user, new EmailVerification($details));
    
    
            return response()->json([
                'message' => 'use created successfully and activation code sent to email',
                'user' => $user,
                'token' => $token,
            ], 201);
        }
    
    

    Activation code trait:

    trait ActivationCode
    {
        public function generateVerificationCode($length = 6) {
            $characters = '0123456789';
            $charactersLength = strlen($characters);
            $code = '';
            for ($i = 0; $i < $length; $i++) {
                $code .= $characters[rand(0, $charactersLength - 1)];
            }
            return $code;
        }
        
    }
    

    EmailVerify Notification :

    class EmailVerification extends Notification
    {
        use Queueable;
        private $details;
    
        public function __construct($details){
            $this->details = $details;
        }
    
        public function via($notifiable)
        {
            return ['mail'];
        }
    
        public function toMail($notifiable)
        {
            return (new MailMessage)
                    ->greeting($this->details['greeting'])
                    ->line($this->details['body'])
                    ->line($this->details['thanks'])
                    ->line($this->details['activation_code']);
        }
    
    
        public function toArray($notifiable)
        {
            return [
                'order_id' => $this->details['order_id']
            ];
        }
    }