Social login with Laravel 5 using multiple providers

27. February 2016 PHP 22
Updated for Laravel 5.3 and 5.4
Implementing social login with Laravel is really easy thanks to their Socialite package. However, many tutorials show how to implement it using just one provider. Even though adding more providers is not hard, it can be a little tricky. You’ll learn how to do it in this step-by-step tutorial.

The code of the finished tutorial is located here: https://github.com/dexbarrett/laravel-socialite-tutorial


Contents


Requirements

Web Server and PHP
We will need a running web server like Apache or Nginx with PHP 5.6.4 or higher. For Windows users I recommend either installing Wamp or using Laravel Homestead.

GIT and Composer

First, make sure you have have Git installed.

If you are using Ubuntu you can install Git from the terminal by running these commands:

Windows users can install Git for Windows

If you already have Git, skip this step 😛

We also need the Composer package manager to install Laravel and other packages we need. If you don’t have it, please refer to the installation guide on the Composer’s website.


Setting up the project

First things first. We need the Laravel framework (of course), so we are going to create a new Laravel project using Composer. If you already have an existing project you can skip this first step. Run this command in your terminal to get the latest version of Laravel (5.4 at this time of writing):

composer create-project laravel/laravel social-login

However, if you want to install Laravel 5.3, for example, you need to specify the version in the command:

composer create-project laravel/laravel social-login 5.3.*

Note to Windows users: I recommend you make use of the GitBash terminal included in Git for Windows for running commands

Here we just clone the laravel/laravel github repository and install its dependencies in one step. The content will be created within a folder named social-login Feel free to name yours different if you wish. Composer may take a while to install the dependencies, so be patient 😀

Now we need to ensure we give write permissions to the webserver. If you are on Windows or using Homestead it’s very likely you already have permissions. Otherwise you can do something like this if you are on Linux or OSX:

You may need to run these commands with sudo

Next we need to include and install the Socialite package. Run this command on your terminal to require and install the latest version of Socialite:

composer require laravel/socialite

This version of Socialite is compatible with the latest version of Laravel, so to make it work with Laravel 5.3 you need to install Socialite 2.x:

composer require laravel/socialite ~2.0

Once Composer has finished installing Socialite, we need to add its service provider and Facade to our project. Open your editor of choice and open the file located at config/app.php within your Laravel project folder. On that file, look for the providers key and add the following value:

Laravel\Socialite\SocialiteServiceProvider::class

That part of the file should look something like this:

On that same file, look for the aliases key and add the following:

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

And that part should look like this:


Creating the layout

We already installed Laravel and Socialite. Now, before diving into the configuration of the social providers, let’s create something we can take a look at!

I must confess I suck at design, but that’s no excuse for making your app look like it’s 1995. We will use Twitter Bootstrap, Font Awesome and Bootstrap Social Buttons to give our app a less crappy look.

Master! Master!

Let’s create a master view or layout. This will contain common markup for the different sections of the app. Each section will inherit that markup and add its own content.

Within the folder located at resources/views create a new folder called layouts. And, within this new folder, create a new file called master.blade.php. This new view file should contain the following (you can double-click inside the code block sections to be able to select the content):

This master template includes the styles and scripts for Bootstrap, Font Awesome and Bootstrap Social Buttons from a CDN. The markup is a common template that has a navbar fixed on top of the page.

The home view

Next, create a new file within resources/views called home.blade.php. This new view should contain the following:

This is the home page of the app. It inherits the contents from the master template. We indicate this using the @extends directive and specifying the path of the view, relative to the resources/views folder. In this case, the master view is within the layouts folder, that’s why we use that in the path. Notice too that subfolders can be indicated using “dot syntax”. Forward slashes are valid too, if you prefer to use them.

The @section directive is used to fill a section defined elsewhere (in this case, the master template). This is similar to class inheritance in OOP.

We use the @section directive to indicate the page-title and page-content sections. The content of a section can be defined inline or as a block. In the latter case it must be ended with a @stop directive.

Now, modify the default route in the file routes/web.php so that it points to home.blade.php view:

When passing the name of a view is not necessary to specify the .blade.php extension

Go to the root of your site on the browser. I created a local site called social-login.dev so I’ll go to http://social-login.dev

The home view should appear, like this.

home-page

The login view

Create a new view file within resources/views called login.blade.php. This view should contain the following:

This view extends from the master layout as well. The content of the page-content section adds three buttons, one for each of the providers we’ll be using: Facebook, Google and Twitter. The markup of these buttons uses the Font Awesome and Bootstrap Social Buttons classes to give the proper look and feel.

Before adding a new route for this view, create a controller. We will use this controller to process the login logic, instead of having all that in anonymous functions.

We can create the controller using the terminal by issuing the following command:

php artisan make:controller LoginController

This will create a file within app/Http/Controllers called LoginController.php. Open that file and add a new method called showLoginPage:

Now, in routes/web.php add a new route called login and point it to the showLoginPage of LoginController:

Now, if you go to /login on your site you should see a page like this:

login-page

Right now, the Sign In link on the navbar points nowhere. Make a small modification to the master.blade.php file so that it resolves to the route corresponding to the showLoginPage (the /login route):

Now, clicking the Sign In link will navigate to the login page.


Creating and configuring the provider applications

We have the UI of the app, but it doesn’t do anything yet. In order to implement social login with Socialite, we first need to create a new application on each provider. To be able to do this, we need to have an account on each service. If you don’t have an account, create one following the corresponding link.

Create a Facebook account
Create a Google account
Create a Twitter account

Creating a Facebook application

To create a Facebook application go to https://developers.facebook.com/apps/ and login with your Facebook credentials. On the new screen that appears click the Add a New App button:

fb-add-new-app

A modal dialog will appear asking you basic information such as your application name, category and an email address for contact. Fill this information accordingly and click Create App ID.

You may need need to enter a captcha after this, do it and you will be taken to your app’s admin page.

On this page, click settings located on the left menu and you will be shown the following screen:

Click the Add Platform button and select Website on the new dialog that appears.

A new section called Website will be added at the bottom:

Fill the input with the name of your host followed by: /login/facebook/callback. We haven’t defined these routes yet in Laravel, but will do later. All the provider routes will follow the same pattern: /login/provider/callback.

Finally, click the Save Changes button located at the bottom right.

Laravel provider configuration

Copy the values shown in your Facebook app settings: App ID and App Secret (for the App Secret to show click the Show button). Now edit the config/services.php file and add a new key called facebook to the array with the following values:

Now edit your .env file and add the following lines:


Creating a Google application

To create a Google app, go to https://console.developers.google.com/iam-admin/projects and sign in with a Google account. On this screen click the CREATE PROJECT button located on the top menu bar:

google-create-project

You will be presented with a new screen to give your proyect a name. This is not what will be shown to users on the authentication screen; this is just an identifier for you. The name displayed is configured later.

Click Create and your project will be created. You’ll be redirected to the API Manager section. Here, you need to enable the Google+ API. Just type Google+ on the search field and select it from the results:

Once you select it, this screen will appear. Just click the Enable button:

Once the Google+ API has been enabled, go to the Credentials section using the link located on the left menu. On this new screen, select the Oauth Consent screen tab and this will be shown:

google-oauth-screen

This section allows you to customize the information that will be presented to the user when they are prompted to authorize this app. You can set things like the homepage URL and the logo image, but these are optional. The required field here is the one labeled Product name shown to users. If you don’t fill this field, you won’t be allowed to generate credentials for the app.

Click the Save button and you will be taken to the Credentials tab of this section. On the right side of this screen you will see a Credentials dialog. Click on the Create credentials dropdown and select OAuth client ID from the options:

google-create-credentials

On this new screen you need to select the application type:

google-application-type-oauth

Select the Web application option and the screen will expand showing the settings required for this type of app:

google-create-client-id

On this new section you can change the name field if you want, but it’s just an identifier. However, you must add the callback URL on the Authorized redirect URIs field. This is the URL where users will be taken once they accept/deny the Google OAuth app.

Add the following to the Authorized redirect URIs field: http://yourlaravelsite.domain/login/google/callback

Click Create and you will be presented with a dialog showing (finally!) the app credentials:

google-app-credentials

You can copy this values now, but they can be accessed from the credentials link on the left menu too. On the screen that appears just select the client name:

google-client-list

And a new screen will show the credentials:

google-app-credentials-manage

Adding the Google credentials to Laravel

Now that you have the Google app credentials, edit the file config/services.php and add a new key named google with the following values:

After that, edit your .env file and add the following lines:


Creating a Twitter Application

To create a Twitter application go to https://apps.twitter.com and sign in with a Twitter account. You will be redirected to the application management screen:

twitter-manage-apps


Click the Create New App button and the next screen will be shown:

This will ask for basic information for the app like the name and description. These fields are required but the one you must fill in is the one for the callback URL. Add the following URL, replacing the hostname with your laravel hostname: http://yourlaravelsite.domain/login/twitter/callback

Once you filled the information, tick the Yes, I agree checkbox and click the Create your twitter application button.

You will be redirected to the new app’s management page:

twitter-app-details

By default, Twitter won’t give your app access to the user’s email, but this permission is easy to get. First, go to the Settings tab on your app’s management page and look for the Privacy Policy URL and Terms of Service URL fields. You need to fill these or Twitter won’t let your app get the email permission. For now, just enter your app’s domain. Once your app is live you need to create these pages and update the urls:

Click the Update Settings button at the bottom and now go to the Permissions Tab.

Unless your app needs to post tweets on behalf of the user, change the permissions to Read only. Then go to the Additional Permissions section below and check the box that says Request email addresses from users. Click the Update Settings button to save the changes.

If you didn’t copy your app’s Consumer Key and Consumer Secret before, go to the Keys and Access Tokens tab and copy these values. Now, edit the config/services.php file and add a new key called twitter to the array with the following:

Then, edit the .env file and add these lines:

Authentication and login

We have the provider configuration set. Now we need to create the code… not so fast; let’s create some database migrations first!

Laravel already includes a user migration. Locate the file in database/migrations called something like 2014_10_12_000000_create_users_table.php and edit it so that it looks like this:

Now we need a new migration to store information related to social providers. Run the following command in your terminal to create it:

php artisan make:migration create_social_login_profiles_table --create=social_login_profiles

This will create a new file within database/migrations called something like 2016_06_03_204539_create_social_login_profiles_table.php

Edit the new migration file and make it look like this:

Connect to your local MySQL server through the terminal or using a client like MySQL Workbench, HeidiSQL (Windows only) or Sequel Pro (Mac only).

Once you are connected, create a new database called social_login

Important: for development we will use the default root credentials to connect to the database. For a live app don’t do this! Instead, create a new user and give it permissions to the database.

Now we have to update the .env and add the database connection details. Locate the following lines on that file and edit accordingly:

Now execute this command to run the migrations and create the tables:

php artisan migrate

Coding Time

Login with the social providers consists of these steps:

  • In our web application, the user selects to login with one of the providers and gets redirected to the provider’s authentication page
  • The user accepts/denies the provider app and is taken back to our web app’s callback URL
  • Our web application handles the success/failure of the authentication

Let’s start by creating a new model that will represent the social login providers. To do this, run the following artisan command:

php artisan make:model SocialLoginProfile

Now, edit the User model located at app/User.php to create a relationship method pointing to the SocialLoginProfile model. We’ll also modify the $fillable array to specify which model fields can be mass-assigned when creating a new model instance:

Now we will create a new class that will handle the social authentication. We do this so we don’t have all the code inside the LoginController.

Create a new file within the app directory and call it LoginUser.php. It should contain the following:

What does this class do?

We first import the classes we are going to need. We import the Socialite facade and a custom exception class (that we’ll create shortly).

Then we have two methods. The authenticate method makes use of the driver method of Socialite, which takes a string representing the social provider we want to use. It’s important to note that this method expects a value matching a supported provider. So, if we want to authenticate with facebook we should pass the string facebook; for Google we pass google and so on. In this case we define a parameter called $provider which we will pass to the Socialite’s driver method and then chain the redirect method which will take the user to the appropiate authentication page.

The login method has more logic as you can see. It accepts a $provider parameter that we’ll pass to Socialite as well.

We begin by defining a try…catch block. We do this because if Socialite fails to properly authenticate the user, it will throw an exception. We will catch a general Exception because Socialite can throw different types of exceptions, but no matter which one it throws, we’ll handle it as a failed authentication. Still, we will throw a custom SocialAuthException that will be intercepted by our LoginController.

The first thing we do inside the try block is to retreive an instance of the corresponding provider using the driver method and chaining the user method. The user method returns an instance of LaravelSocialiteOneUser or LaravelSocialiteTwoUser depending on the provider. This User class encapsulates user data obtained from the provider such as email, nickname, name and avatar.

Once we have the user data, we use the User model’s firstOrCreate method to look for a record in the users table that matches the email obtained from the provider. If a record is found, it will return the corresponding User model; otherwise it will create a new record on the users table and return a User model as well.

Then we use the socialProfile dynamic property in the User model to check if the relationship exists. We use a the ternary shortcut operator to test this: if the property socialProfile returns a non-null value, that will be assigned to $socialProfile; otherwise, a new instance of the SocialLoginProfile will be created and assigned to $socialProfile. It’s important to note that the property name is socialProfile which is different to the model name SocialLoginProfile. This is because the property name is based on the method declared in the User model, and SocialLoginProfile is the actual model where the method socialProfile() in app/User.php points to.

We do this to see if this is the first time a user logs into the application and create the appropiate records in the database.

We then update the SocialLoginProfile model instance and dinamically set the field corresponding to the provider the user used to authenticate. We set its value equal to the ID obtained from the provider and save it to the database.

Finally, we use the auth() helper method to obtain access to the Laravel’s authentication class and we use its login method, which accepts a User model as a parameter and logs it into our application.

No we have the core of the social login process ready. But we need some more work to do to wire things up. First, let’s create the custom exception class that we mentioned before.

Create a new file within the app/Exceptions folder and call it SocialAuthException.php. It will contain the following:

Now let’s move into view territory. We need to create a page that will simulate the dashboard.

Create a new file within resources/views and call it dashboard.blade.php. It should contain the following:

Simple stuff. Just a view extending the master layout, with a header.

Now let’s make a new route to show this new view. Edit the routes/web.php file and add the following:

Now edit the app/Http/Controllers/LoginController.php file and add the corresponding method:

If you navigate to http://yourLaravelSite.domain/dashboard you should see the dashboard view:

dashboard-page

But there’s a small problem: this view should be available to logged users only. Let’s fix that!

First edit the file at app/Exceptions/Handler.php and change the unauthenticated method so that it points to the route mapped to LoginController@showLoginPage. This is where not-logged users will be redirected when trying to access a protected page:

Now let’s protect the dashboard page using this middleware. Edit the routes/web file and modify the dashboard route like this:

Now, if we you go to http://yourLaravelSite.domain/dashboard it will redirect you to the login page.

We already have the LoginUser, class but our LoginController doesn’t know about it. Let’s modify our app/Http/Controllers/LoginController.php file and make it use the LoginUser class:

What we do first is to import the LoginUser and SocialAuthException classes. Then we define the constructor method which takes the LoginUser class as a parameter and stores the reference into a class property called $loginUser. When the controller runs, Laravel will automatically inject an instance of the LoginUser class and it will be available in the class property.

Then we have a method called auth which simply calls the authenticate method of the LoginUser class. This is where we redirect the user to the provider’s authentication page.

We have another method, called login, which uses the LoginUser class as well. This method will run when the user comes back from the provider’s authentication page.

Here we have a try…catch block. If all goes well, the user will be logged in and redirected to the dashboard page. But if something goes wrong, the SocialAuthException will be thrown and the catch block will be executed. The catch block redirects the user to the login page and injects a message into the session informing that the authentication failed.

We also have a logout method. This simply uses Laravel’s authentication to logout the user and redirects to the home page.

Bear with me; we’re almost done.


The controller now uses the LoginUser class, but we haven’t created the corresponding routes. So, edit the routes/web.php file and add this:

Here we add a route to the logout method of the LoginController, and other two routes that point to the auth and login methods, respectively.

These last two routes accept a parameter called provider. This dynamic value will contain the provider that we want to use, and this value will be passed to the controller method mapped to the route. We also add some constraints to those two routes. We use a regex saying that the provider parameter can only contain one of the following strings: facebook, google or twitter. Any other value will not match the route pattern and will result in a 404 error.

Now let’s update our login page buttons so they point to their corresponding provider. Edit the resources/views/login.blade.php file and change accordingly:

We use the action() helper to point to the route mapped to the auth method of the LoginController. But since this route requires a dynamic parameter, we need to pass it too. We do this by using an array as a second parameter in the action() helper, using a key that matches the route parameter and the corresponding provider as the value.

Remember that the LoginController will redirect the user to the login page if the authentication fails. It will store a message in the session too. We need to update our login view to display this message.

First, we will need a partial view. A view that only will contain the markup that display the error messages.

Create a new folder within resources/views called partials. Inside this folder create a new file called flash-messages.blade.php which should have the folloing content:

This view uses the session() helper to access data stored in the session. The has method returns true if a value with the key flash-message exists. If it does, it will show the markup and will access the value using the has method of the Session class.

Now we will update the login page to include this partial view.

Edit the resources/views/login.blade.php file and update accordingly:

We use the Blade’s include() directive to insert the content of the partial view we created before.

And talking about partials, we’ll need another one. Why? We are going to create a small dropdown on the navbar that displays the email of the logged in user and contains a link to go to the dashboard and another link to sign out.

Create a new file at resources/views/partials called user-menu.blade.php. It should contain this:

This partial uses the user method of Laravel’s authentication class. This method returns an instance of the user model if the user is logged in. So, this dropdown should only appear if the user is logged in, right? Right!

To do this, we need to edit the resources/views/layouts/master.blade.php file and modify it like this:

Once again, we use the auth() helper to use Laravel’s authentication goodies. The check method returns true if the user is logged in. So, if the user is logged in, it includes the contents of the partial view that contains the menu markup. Otherwise, it display the link that takes to the login page.

And we’re done with the changes. Let’s try this out!


For example, click on login with Google. The authentication page will appear:

google-authorization-screen

If you click on Deny, you’ll be redirected back to the login page and an error message will be shown:

failed-auth-message

But now, click again login with Google and allow access to the application. You’ll be logged into the web app and redirected to the dashboard:

logged-in-dashboard

If you click the Sign out link from the dropdown, you will be logged out and redirected to the home page.


Aaaaand pretty much that’s it! We’re finally done. I really hope this helped you if you had trouble implementining Socialite in your Laravel application.

Next, I included a troubleshooting section with common problems that you may encounter and the solutions.

The code of the finished tutorial is here https://github.com/dexbarrett/laravel-socialite-tutorial


Troubleshooting

  • You get an error like this cURL error 60: SSL certificate: unable to get local issuer certificate

This is most likely to happen if you are on Windows. To fix this problem first download and extract this cert file and place in a proper location. For example, if you are using WAMP, place it in C:\wamp\bin\php\yourVersionOfPHP\extras\ssl.

Now, open your php.ini file and look for the line that says curl.cainfo and point it to the path of your extracted cacert.pem file. Remove the semicolon at the beginning of the line to uncomment it.

curl.cainfo = "C:\wamp\bin\php\yourPHPVersionHere\extras\ssl\cacert.pem"

Make sure the php.ini file you edit is the one that the web server uses and not the one for the command line. At least in WAMP there are two of them. In WAMP, this php.ini is normally located at C:\wamp\bin\apache\apacheVersionHere\bin.

Save the file and restart Apache.


  • When you attempt to authenticate, you receive an error saying something like there’s an URL mismatch or some voodoo like that

This is likely to happen when the hostname you use for development is different to the hostname of the live site. You can use the same host for both development and production to avoid this. But if you don’t, make sure to update the callback URLs on each provider. For example, for the tutorial I used a local site named http://social-login.dev. But if I wanted to deploy this site I would probably use a different hostname. Maybe something like http://backendtime-social-demo.com. So I should go to each provider’s app settings and change the callback URLs so that they start with http://backendtime-social-demo.com.

In the particular case of Facebook, when you plan to deploy, you first need to make your Facebook app public. Log into https://developers.facebook.com/apps/ and select your app. Once you are in its admin area, go to the section named App Review on the left menu:

facebook-appreview-menu

On this area there will a section like this:

facebook-make-app-public

Just click the switch button and a dialog will appear. Click Confirm and your app will be in live mode.

facebook-public-modal

This is not required for development, like I said. Just don’t forget to do this when you are about to first deploy to production or your users won’t be able to sign in with Facebook.


What do you think of the tutorial? Is there any other Laravel/PHP topic you’d like to learn about? Leave a comment and let me know.