Laravel 5.2 Socialite Facebook Login

Update March 30, 2017 If you having problems with facebook login make sure to update your Socialite package with composer update laravel/socialite.

This is a tutorial of integrating Laravel Socialite plugin using Facebook.

I will be using Laravel 5.2. I assume you have experiance with Laravel. With make:auth we will create basic scaffold for login and registration views and routes.

Preparing

Lets begin with the clean laravel installation. In terminal type:

laravel new awesome-app
cd awesome-app

Create database:

echo "create database awesome_app" | mysql -u root -p

# type password

On local environment I'm using root user for mysql but you change it accorting to your setup. Fill in .env with credentials and finally migrate database.

php artisan migrate

Create the scaffold for login with

php artisan make:auth   

Start the server with php artisan serve and visit the page http://localhost:8000/.

Installing Socialite

Socialite is a package that makes building authentification with popular social networks simple.

Install it with composer:

composer require laravel/socialite

... and by following the instructions from github page add provider to the config/app.php file

'providers' => [
    // Other service providers...

    Laravel\Socialite\SocialiteServiceProvider::class,
],

and alias

'Socialite' => Laravel\Socialite\Facades\Socialite::class,

now our app is ready for accepting users from other services.

Facebook Login

Let's start with facebook since it is most common requirement. You need to have developers account on facebook to be able to create apps. I don't remember when and how I did register that but a little googling can help.

Open Facebook page for developers

Hover over My Apps and click on Add a New App and select Website.

Enter the app name. On the next step select the category and click Create App ID

When you get to the next step, select Skip Quick Start because it is showing instructions of how to login users with JavaScript SDK. With Socialite we are not using that.

On the app dashboard you will see all important data that we need for configuring Laravel Socialite. In the config/services.php add credentials for facebook:

Value from field App ID put in client_id, from App Secret to client_secret and redirect we will leave empty for now. Configuration will look like this.

'facebook' => [
    'client_id' => '690344774435367',
    'client_secret' => 'ebc50d3fd1d2f7286e02d247e5751ef4',
    'redirect' => '',
],

Next, we need following methods in our app that will

  • redirect our users to the facebook.

  • handle callback from facebook

For that we need controller.

php artisan make:controller SocialAuthController    

Add two methods redirect() and callback()

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use Socialite;

class SocialAuthController extends Controller
{
    public function redirect()
    {
        return Socialite::driver('facebook')->redirect();   
    }   

    public function callback()
    {
        // when facebook call us a with token   
    }
}

Now register those methods in routes.php file in the group that has web middleware (we need session).

Update: It looks like web middleware is applied by default since 5.2.31 version of Laravel. So do not create a group with a web middleware.

Route::get('/redirect', 'SocialAuthController@redirect');
Route::get('/callback', 'SocialAuthController@callback');

Now this is a part when we need to go back to the facebook configuration in service.php and update redirect url.

We are going to set this field to full url of the callback route. In our case when we are using PHP development server which is served on http://localhost:8000 by default, our final config will look like:

'facebook' => [
    'client_id' => '690344774435367',
    'client_secret' => 'ebc50d3fd1d2f7286e02d247e5751ef4',
    'redirect' => 'http://localhost:8000/callback',
],

This will be changed for production, and you should make use of the env() helper function.

One more thing before proceeding is to register our development site url (which is in this case http://localhost:8000) with facebook app. Back to the Developer's dashboard. Click on Settings and Add Platform. Choose Website and type your development domain (http://localhost:8000). Save Changes.

Now we need to add link to our redirect route which will further redirect user to the facebook. Open resources/views/auth/login.blade.php and add simple link under the Forgot Your Password link.

<a href="redirect">FB Login</a>

Now in addition to normal login, we have Facebook login link.

Now you can test the link. Fist time, you will need to confirm that you allow this app to use your data.

After confirmation, you will be returned back to the app. You can see, that url contains code which is a kind of temporary username and password that allow us to ask facebook for more data about the user.

Socialite again handles this automatically. In the callback method add following:

$providerUser = \Socialite::driver('facebook')->user(); 

This user is different from User in our system, but it has enough data for us to create and authenticate the user in our app. It provides following methods: getId(), getNickname(), getName(), getEmail(), getAvatar().

And with this we are done with Socialite and facebook. Next step is to integrate facebook users with our system.

Integrating Facebook users with our app

This solution is designed with a goal to allow multiple social accounts to be connected to the single account in our app. Note that some providers do not return email address, so email cannot be not null and we will change that.

So set the email to be nullable in create_users_table migration:

$table->string('email')->unique()->nullable();

We will need additional migration and model for social accounts.

php artisan make:migration create_social_accounts_table --create="social_accounts"

php artisan make:model SocialAccount

Add following fields to the migration:

    Schema::create('social_accounts', function (Blueprint $table) {
        $table->integer('user_id');
        $table->string('provider_user_id');
        $table->string('provider');
        $table->timestamps();
    });

Run the migrations php artisan migrate:refresh

Column provider_user_id is facebook's user id, and provider in this case will be always facebook but it will allow us to add additional providers (twitter, linkedin, ...) later.

Add relations and fillable to the SocialAccount model.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class SocialAccount extends Model
{
    protected $fillable = ['user_id', 'provider_user_id', 'provider'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Now we need some handling service that will try to register user or log in if account already exists. Create SocialAccountService.php in the app folder and put following content:

<?php

namespace App;

use Laravel\Socialite\Contracts\User as ProviderUser;

class SocialAccountService
{
    public function createOrGetUser(ProviderUser $providerUser)
    {
        $account = SocialAccount::whereProvider('facebook')
            ->whereProviderUserId($providerUser->getId())
            ->first();

        if ($account) {
            return $account->user;
        } else {

            $account = new SocialAccount([
                'provider_user_id' => $providerUser->getId(),
                'provider' => 'facebook'
            ]);

            $user = User::whereEmail($providerUser->getEmail())->first();

            if (!$user) {

                $user = User::create([
                    'email' => $providerUser->getEmail(),
                    'name' => $providerUser->getName(),
                ]);
            }

            $account->user()->associate($user);
            $account->save();

            return $user;

        }

    }
}

This will try to find provider's account in the system and if it is not present it will create new user. This method will also try to associate social account with the email address in case that user already has an account.

Now everything is ready to handle facebook's callback to our app.

Open SocialAuthController and with updated callback method it should look like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\SocialAccountService;
use Socialite;

class SocialAuthController extends Controller
{
    public function redirect()
    {
        return Socialite::driver('facebook')->redirect();   
    }   

    public function callback(SocialAccountService $service)
    {
        $user = $service->createOrGetUser(Socialite::driver('facebook')->user());

        auth()->login($user);

        return redirect()->to('/home');
    }
}

Now when we login click FB Login this time we are redirected and logged in.

In my next post I will add Twitter as a login option.

Update: changed Schema::table to Schema::create tnx to Adam Esterle

Author

I plan to write more articles about common laravel components. If you are interested let’s stay in touch.
comments powered by Disqus