Sidenote: This part of the tutorial is technically finished, but requires polishing to reach quality of other tutorials on this blog. Thanks for understanding. Enjoy the code.

Welcome to the part 2 of user authentication when we will setup a no-password alternative to the standalone user log in process introduced in part 1.

If you didn’t follow the first part of his tutorial I encourage to check this out as we will be starting from the point where user sign in form and SMTP mailing are already set up, also following the naming from previous examples. Other than that, we should be ready to go.

Show them options

Let’s start with the view. At the moment, your view should include e-mail(or name or nickname, depends on preferences) and password field with optional “Remember me” check-box we all know from log in forms, followed by Submit/Sing In/ Log In button. However, in the e-mail authentication, you don’t want to use these 2 fields to take from user the burden of remembering his password. There are 2 ways of solving it.

You could make password feel optional (as not required in your validation) and if password is missing, but e-mail is valid, send an authentication message or add an extra field, exclusively for e-mail. Option two is more elegant in my opinion, but if you don’t have e-mail field, you may want to go with the 2nd scenario; the real difference is in controller, when you will check for submitted option and use it as a condition, so both way will be covered.

Scenario 1

/*app/views/login.blade.php*/

@if(Auth::check())
	<p>You are already logged in. Enjoy it!</p>
@else				
	{{ Form::open( array('url' => 'login', 'role' => 'form') ) }}
	
	<div class="form-group">
		{{ Form::label('email', 'E-mail') }}
		{{ Form::email('email', '') }}
	</div>
	<div class="form-group">
		{{ Form::label('password', 'Password') }}
		{{ Form::password('password') }}
	</div>
		
	{{ Form::submit('Sign In') }}
	{{ Form::close() }}
@endif
Scenario 2

/*app/views/login.blade.php*/

@if(Auth::check())
	<p>You are already logged in. Enjoy it!</p>
@else				
	{{ Form::open( array('url' => 'login', 'role' => 'form') ) }}
	
	<div class="form-group">
		{{ Form::label('nick', 'Nick') }}
		{{ Form::text('nick', '') }}
	</div>
	<div class="form-group">
		{{ Form::label('password', 'Password') }}
		{{ Form::password('password') }}
	</div>
	<p>or</p>
	<div class="form-group">
		{{ Form::label('email', 'E-mail') }}
		{{ Form::email('email', '') }}
	</div>
		
	{{ Form::submit('Sign In') }}
	{{ Form::close() }}
@endif

Process choices

Now it’s time to move to the controller. Continuing your work from previous part of the tutorial, you just need to edit AuthController.

Sidenote: Below I will make controller for Secenario 1, but following this example you can easily adjust condition to fit extra field.

/*controllers/AuthController.php*/

public function loginView()
{
	return View::make('auth.login', array('pageTitle' => 'Login'));	
}

public function loginTry()
{
	Input::only('email', 'password');
	
	$email = Input::get('email');
	$password = Input::get('password');
	
	// Set login credentials
	$credentials = array(
	'email'    => Input::get('email'),
	'password' => Input::get('password'),
	);
	
	if(email && password){
		// Try to authenticate the user
		$user = Auth::attempt($credentials);
		
		return Redirect::intended('/login');
	}
	else if(email && !password){
		
		$auth_code = uniqid(rand(1000, 6000), true);
		Session::put($email, $auth_code);
		
                Mail::send('email.auth', array('auth_code' => $auth_code, 'email' => $email), function($message)
                            {
                                $message->to(Input::get('email'))->subject('Authentication');
                            });

				
		return Redirect::intended('/login');
	}
	else{
		return Redirect::intended('/login');
	}		
}

As you can you see, simple condition determines if user decided to use password or not. Of course you can do it in many other ways (it shouldn’t be possible to spam user with messages by not providing a password), but since we are focused on authentication mechanism, we will go with this simple example.

Second condition is what this part of the tutorial is all about. There is no password provided, but user submitted an e-mail. In this situation the controller with generate $auth_code [authentication code] and store it with e-mail address in the session. After that, $auth_code is send to our user’s e-mail address as a part of authentication URL. Let’s see the e-mail view and create a proper route.

 
/*views/authenticate.blade.php*/
<!DOCTYPE html>
<html lang="en-US">
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		<div>			
			<p>Activation code: {{ URL::to('/authenticate', array($email, $activation_code)) }}</p>
		</div>
	</body>
</html>
 
/*app/routes.php*/

	Route::get('authenticate/{email}/{auth_code}', array('uses' =>'AuthController@authEmail')); 

	Route::get('login', array('uses' =>'AuthController@loginView'));
	Route::post('login', array('before' => 'csrf' ,'uses' =>'AuthController@loginTry'));

Sidenote: If you would prefer your URL to be structure differently, like /?email=…&auth_code= feel free to adjust the code, but keep in mind to edit controller and function picking the variables up.

Finally you are ready to make controller function to authenticate user who visited your carefully crafted URL.

/*controllers/AuthController.php*/

    public function authEmail($email, $auth_code)
    {
        $stored_auth_code = Session::get($email); 

        if ($auth_code === $stored_auth_code)
        {

           	$user = User::where('email','=',$email);
		Auth::login($user);
		
		return Redirect::intended('/login');
        }
        return Redirect::intended('/login');
    }

public function loginView()
{
	return View::make('auth.login', array('pageTitle' => 'Login'));	
}

public function loginTry()
{
	Input::only('email', 'password');
	
	$email = Input::get('email');
	$password = Input::get('password');
	
	// Set login credentials
	$credentials = array(
	'email'    => Input::get('email'),
	'password' => Input::get('password'),
	);
	
	if(email && password){
		// Try to authenticate the user
		$user = Auth::attempt($credentials);
		
		return Redirect::intended('/login');
	}
	else if(email && !password){
		
		$auth_code = uniqid(rand(1000, 6000), true);
		Session::put($email, $auth_code);
		
                Mail::send('email.auth', array('auth_code' => $auth_code, 'email' => $email), function($message)
                            {
                                $message->to(Input::get('email'))->subject('Authentication');
                            });

				
		return Redirect::intended('/login');
	}
	else{
		return Redirect::intended('/login');
	}		
}

Final step, $email and $auth_code from the URL are passed to the controller function responsible for authentication based on email address. First it fetches code associated with provided email address and checks if it’s similar with code provided via URL. If they are identical, user will be found based on email address and logged in manually.