Friday 22 January 2016

Email verification at the time of signup a user with Laravel 5.1

Hi Everybody,

There are a number of situations in which it is beneficial to get a newly registered user of your site to verify their email address. We will talk about when you should include this functionality and then implement email verification within a Laravel app.


User Table Migration

We need to create just two fields in addition to the fields that are standard in most users tables (username, email, password, etc.). Firstly, we need a boolean field 'confirmed' to keep track of whether a user has confirmed their email address, this will be set to false by default.

The second field that we require is a confirmation_code string field. When a user is signed up we set this field to a random string, an email is then sent to the user asking them to confirm their account by following a link to /register/verify. When a user follows this link, we take the passed in confirmation code and search for it within the users table. If a matching confirmation code is found we set the confirmed field for this user to true and set the confirmation code to null.

The migration below is for a very basic user table. Notice that the confirmed field is set to false by default and that the confirmation_code is nullable.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateUsersTable extends Migration {

    public function up()
    {
        Schema::create('users', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('username')->unique();
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('confirmed')->default(0);
            $table->string('confirmation_code')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('users');
    }
}

Registering A User

After validation, we need to create the user, just remember that if you are use mass assignment with User::create(), you need to set the $fillable property on your user model to contain username, email, password and confirmation code.

Once the user has been created the only thing left to do is to send them their confirmation email. As stated earlier the convention that we are going to follow for the confirmation route is /register/verify/{confirmation_code}. We will use Laravel's Mail::send() and create a very basic email template that will take the confirmation code and output a link to the confirmation url that the user needs to visit.

<?php

use Illuminate\Support\Facades\Input;

use Illuminate\Support\Facades\Session;
use App\Http\Requests;

class RegistrationController extends \BaseController {

    public function store(Request $request)
    {
        $rules = [
            'username' => 'required|min:6|unique:users',
            'email' => 'required|email|unique:users',
            'password' => 'required|confirmed|min:6'
        ];

        $input = Input::only(
            'username',
            'email',
            'password',
            'password_confirmation'
        );

        $validator = Validator::make($input, $rules);

        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
        }

        $confirmation_code = str_random(30);

        User::create([
            'username' => Input::get('username'),
            'email' => Input::get('email'),
            'password' => Hash::make(Input::get('password')),
            'confirmation_code' => $confirmation_code
        ]);

        Mail::send('email.verify', $confirmation_code, function($message) {
            $message->to(Input::get('email'), Input::get('username'))
                ->subject('Verify your email address');
        });

       $request->session()->flash('status', 'Thanks for signing up! Please check your email.');
       return redirect()->intended("login");
    }
}

and the simple verification email using blade:

<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h2>Verify Your Email Address</h2>

        <div>
            Thanks for creating an account with the verification demo app.
            Please follow the link below to verify your email address
            {{ URL::to('register/verify/?confirm_code=' . $confirmation_code) }}.<br/>

        </div>

    </body>
</html>

Confirming The User

To complete the confirmation process the user must follow the link that is sent to them in their welcome email. A corresponding entry should be included in the routes.php file:

Route::get('register/verify', [
    'as' => 'confirmation_path',
    'uses' => 'RegistrationController@confirm'
]);

<?php
use Illuminate\Support\Facades\Session;
use App\Http\Requests;
class RegistrationController extends \BaseController {

    public function confirm(Requests $request)
    {
         $data = $request->all();
        $confirmation_code = $data['confirm_code'] ;
        if( ! $confirmation_code)
        {
          $request->session()->flash('status', 'Invalid link.');
          return redirect()->intended("login");
        }

        $user = User::whereConfirmationCode($confirmation_code)->first();

        if ( ! $user)
        {
          $request->session()->flash('status', 'Invalid link.');
          return redirect()->intended("login");
        }

        $user->confirmed = 1;
        $user->confirmation_code = null;
        $user->save();
        $request->session()->flash('status', 'You have successfully verified your account.');
        return redirect()->intended("home");
    }
}

Logging A User In

The only addition that needs to be made to the authentication system is to check whether the user is confirmed prior to logging in. Laravel's Auth::attempt() function can take extra conditions which must be true for a successful login attempt. This means we can add 'confirmed' => 1 to our credentials array to ensure that a user is confirmed before they can log in.

<?php
use Illuminate\Support\Facades\Session;
use App\Http\Requests;

class SessionsController extends \BaseController {

    public function store(Requests $request)
    {
        $rules = [
            'username' => 'required|exists:users',
            'password' => 'required'
        ];

        $input = Input::only('username', 'email', 'password');

        $validator = Validator::make($input, $rules);

        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
        }

        $credentials = [
            'username' => Input::get('username'),
            'password' => Input::get('password'),
            'confirmed' => 1
        ];

        if ( ! Auth::attempt($credentials))
        {
             $request->session()->flash('status', 'We were unable to sign you in.');
            return redirect()->intended("login");
        }

        $request->session()->flash('status', 'Welcome Back.');
        return redirect()->intended("home");
    }
}

Note:-In above code we have used session to display message in view(in login and home view).
So we need below code in both views

@if(Session::has('status'))
    <div class="alert alert-success"><span class="glyphicon glyphicon-ok"></span><em> {!! session('status') !!}</em></div>
@endif

If you have any questions then please leave a comment below and I will do my best to help out.

No comments:

Post a Comment