Jun 02, 2014
Table of contents:
Last week I looked at authenticating a user through a social provider. In my example I was using Twitter, but there are many different social authentication providers available that all use the common protocol of Oauth.
In last week’s tutorial I left it at the stage where we have successfully authenticated a user through a social provider and we now have an object that contains some limited data about the user.
In this week’s tutorial I’m going to finish off the registration process by implementing a new child class of the registrator service from a couple of weeks ago.
When you retrieve user data from the Twitter api, you will only be returned data that is already publicly available. This means you won’t automatically get the user’s email address.
In order to get the user’s email address, you have to request it for yourself as part of your application’s registration process.
So once the user has successfully authenticated via Twitter and I have collected the user’s oauth_token
and oauth_token_secret
, I’m going to redirect the user to a form so they can finish off registering for Cribbb.
The first thing to do is to create a new route and add the redirect to the callback()
method on the AuthenticateController
from last week:
Route::get("auth/register", [
"uses" => "AuthenticateController@register",
"as" => "authenticate.register",
]);
Note: ensure that you put this route ahead of the auth/{provider}
route in your routes.php
file!
The final callback()
method should look like this:
/**
* Receive the callback from the authentication provider
*
* @return Redirect
*/
public function callback($provider)
{
try
{
$provider = $this->manager->get($provider);
$token = $provider->getTokenCredentials(
Session::get('credentials'),
Input::get('oauth_token'),
Input::get('oauth_verifier')
);
$user = $provider->getUserDetails($token);
Session::put('username', $user->nickname);
Session::put('oauth_token', $token->getIdentifier());
Session::put('oauth_token_secret', $token->getSecret());
Session::save();
return Redirect::route('authenticate.register');
} catch(Exception $e) {
return App::abort(404);
}
}
Next I need to create the register()
method for returning the correct view:
/**
* Return the form so the user can complete their registration
*
* @return View
*/
public function register()
{
return View::make('authenticate.register', ['username' => Session::get('username')]);
}
In this method I’m also passing the username
from the Session. This will allow me to auto populate the form with the user’s Twitter username, however they will also be able to choose a different username by updating the form.
And finally, the view file should look something like this:
{{ Form::open(array('route' => 'authenticate.store')) }}
@if ($errors->any())
<ul>
{{ implode(",", $errors->all('<li>:message</li>'))}}
</ul>
@endif
<div>
{{ Form::label('email', 'Email Address') }}
{{ Form::text('email') }}
</div>
<div>
{{ Form::label('username', 'Username') }}
{{ Form::text('username', $username) }}
</div>
{{ Form::submit('Complete') }}
{{ Form::close() }}
This view file is just your basic Laravel form. Here I’m requesting the user’s username and email address. I don’t need the user’s password because that’s kinda the point of using a social authentication provider.
Next I need to create the route that will accept the POST
request from this form:
Route::post("auth", [
"uses" => "AuthenticateController@store",
"as" => "authenticate.store",
]);
In order to create the new user, I will need to gather data from both the POST
request as well as the Session.
To do this I will create a new store()
method on the AuthenticateController
to accept the POST
request:
/**
* Store the user's details and authenticate on success
*
* @return Redirect
*/
public function store()
{
$data = [
'username' => Input::get('username'),
'email' => Input::get('email'),
'oauth_token' => Session::get('oauth_token'),
'oauth_token_secret' => Session::get('oauth_token_secret')
];
}
The first thing to do in this method is to create a $data
array of the different bits of data from the Input
and the Session
.
In the first tutorial on creating a registration process, I created a CredentialsRegistrator
to register new users with a username, email and password.
To register a user with social provider credentials instead, all I have to do is to create a new child Registrator
class called SocialProviderRegistrator
:
<?php namespace Cribbb\Registrators;
use Cribbb\Repositories\User\UserRepository;
class SocialProviderRegistrator extends AbstractRegistrator implements
Registrator
{
/**
* The UserRepository
*
* @param Cribbb\Repositories\User\UserRepository
*/
protected $userRepository;
/**
* An array of Validable classes
*
* @param array
*/
protected $validators;
/**
* Create a new instance of the CredentialsRegistrator
*
* @return void
*/
public function __construct(
UserRepository $userRepository,
array $validators
) {
parent::__construct();
$this->userRepository = $userRepository;
$this->validators = $validators;
}
/**
* Create a new user entity
*
* @param array $data
* @return Illuminate\Database\Eloquent\Model
*/
public function create(array $data)
{
if ($this->runValidationChecks($data)) {
return $this->userRepository->create($data);
}
}
}
This class is essentially the same as the CredentialsRegistrator
class as the registration process is really quite simple at this stage. Separating the two processes into two different service classes gives us future scope for allowing the two registration processes to diverge.
Registration with a social provider requires that an oauth_token
and oauth_token_secret
are present in the user’s data. To ensure that the data passed to the create()
method meets this requirement, we can create a new Validator
class:
<?php namespace Cribbb\Registrators\Validators;
use Cribbb\Validators\Validable;
use Cribbb\Validators\LaravelValidator;
class OauthTokenValidator extends LaravelValidator implements Validable
{
/**
* Validation rules
*
* @var array
*/
protected $rules = [
"oauth_token" => "required",
"oauth_token_secret" => "required",
];
}
Finally, the SocialProviderRegistrator
can be bound to the IoC container in the RegistratorsServiceProvider
:
/**
* Register the CredentialsRegistrator service
*
* @return void
*/
public function registerSocialProviderRegistrator()
{
$this->app->bind('Cribbb\Registrators\SocialProviderRegistrator', function($app) {
return new SocialProviderRegistrator(
$this->app->make('Cribbb\Repositories\User\UserRepository'),
[
new UsernameValidator($app['validator']),
new EmailValidator($app['validator']),
new OauthTokenValidator($app['validator'])
]
);
});
}
In this example I’m reusing the UsernameValidator
and the EmailValidator
, but I’m also injecting the OauthTokenValidator
too.
With the SocialProviderRegistrator
service created we can now inject it into the AuthenticateController
:
use Cribbb\Authenticators\Manager;
use Cribbb\Registrators\SocialProviderRegistrator;
class AuthenticateController extends BaseController {
/**
* The Provider Manager instance
*
* @param Cribbb\Authenticators\Manager
*/
protected $manager;
/**
* The Registrator instance
*
* @param Cribbb\Registrators\SocialProviderRegistrator
*/
protected $registrator;
/**
* Create a new instance of the AuthenticateController
*
* @param Cribbb\Authenticators\Manager
* @return void
*/
public function __construct(Manager $manager, SocialProviderRegistrator $registrator)
{
$this->beforeFilter('invite');
$this->manager = $manager;
$this->registrator = $registrator;
}
Finally, we can now use the $this->register
instance to create a new user:
/**
* Store the user's details and authenticate on success
*
* @return Redirect
*/
public function store()
{
$data = [
'username' => Input::get('username'),
'email' => Input::get('email'),
'oauth_token' => Session::get('oauth_token'),
'oauth_token_secret' => Session::get('oauth_token_secret')
];
$user = $this->registrator->create($data);
if ($user) {
Auth::login($user);
return Redirect::route('home.index');
}
return Redirect::route('authenticate.register')->withInput()
->withErrors($this->registrator->errors());
}
If a new user is successfully created we can authenticate them and redirect to the main page of the application. If the user’s data is invalid we can redirect back to the form with the data and an array of errors to be displayed that will allow the user to correct their mistakes or try again with different data.
In this tutorial we’ve built upon the foundation from the last couple of weeks to create a process for authenticating with a social provider and registering a user within our own application.
This registration process is greatly simplified because we’ve already done the hard work by building the flexible registration process for registering new users with username and password credentials. This means to add subsequent different types of registration processes, all we need to do is to create the bits that are relevant, rather than reinventing the wheel each time. We can also leverage the same validator classes so we don’t have to duplicate code for each type of registration process.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.