Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Phillipp's avatar

Complete Register and Login App with Socialite and multiple Providers

Hey,

it would be cool if Jeffrey can make some videos to explain the whole process of registering user, store them and remember (login) them with socialite and multiple providers (google, facebook and so on). And if its possible to provide also register and login with normal accounts on the site.

Phillipp

0 likes
34 replies
khoanguyenme's avatar

Almost everything was covered in this video : https://laracasts.com/series/whats-new-in-laravel-5/episodes/9 If you want to use multiple providers? Very easy

Add a new table called user_profiles , user_providers or what ever you name it.

When a user login with Provider, check if that provider is exists on you database. If not, store the new provider. Everything else extractly the same with the video.

4 likes
IsraelOrtuno's avatar

@khoanguyenme that video is really helpful but does not way anything about storing the user data. I want to use socialite for registering users in my app and don't know what data should I store. Should I save the code? The access_token?

1 like
sitesense's avatar

I also looked at Socialite with thoughts of including it in a project alongside traditional login.

I think what you need to consider is that the authentication is done outside of your app.

You can still store the user's email and other data in the users table so that you can include them in your mailing list, but also realise that you need a way to distinguish those users from other users - for example if you needed to tell all users to reset their account password, this would not apply to Socialite users.

You may need to change your existing users table so that the password field is nullable or not required - at least for the scope of a Socialite user.

The "token" and "tokenSecret" may not be required beyond the initial authentication, unless you want to interact with the third party API after the user has logged in. Therefore it may be useful to also store these in the users table or session variable, bearing in mind that they will probably change for each login.

Disclaimer: Just some thoughts since I haven't looked at it in detail and everything I said may be utter rubbish :)

IsraelOrtuno's avatar

@sitesense from what I have read so far about this issue you are probably right or at least, pretty close to be.

I am actually doing exactly what you have just said, storing user details but not password when they register through socialite, this way I can difference between socialite and regular users.

bigblueboss's avatar

@IsraelOrtuno I've done exactly up to the point where I can store information but maybe you can help me out here. When I go through the normal L5 authentication that comes out of the box, I can use Auth::attempt, Auth::check, Auth::user() etc.

But when I do all the authentication on FB, it only returns temporary information. I can, of course, check to see if the "email" return from facebook is the same as an email existing from the database already to determine if it's a new user or an existing user, but even then, how can I make the user officially "logged in" to the site? This is the part that I'm having difficulty with.

For example, let's say the user logs in through facebook, and we are returned a information about him, we check to see if his email is the same as an email written in our users database. Great. Now what though? What if we want to check, on the next page, if this person is logged in or not? I can't use Auth::check in this case right? So how would I do it? (I believe in php one would set a session superglobal and check if the superglobal is set or not, and if it is, use it to query it agains the database, but I'm not sure how to do things in L5 properly with FB login).

bigblueboss's avatar

@khoanguyenme I noticed on a couple other threads that you've already got google and fb logins working, and I thought perhaps you might be able to help me as well?

martinbean's avatar

In my callback method I try and find a matching user in my database, create a User record if one doesn’t already exist, and then log the user in.

The exception is services like Twitter, who don’t provide you with an email address. In this case, you can have a separate table that stores user IDs and their social networking site IDs. Something like this:

Schema::create('user_social_accounts', function(Blueprint $table)
{
    $table->increments('id');
    $table->integer('user_id')->unsigned();
    $table->string('provider'); // Facebook etc.
    $table->string('provider_uid'); // Facebook profile ID, Twitter user ID etc.
}

Then in your callback, check if there’s a record in this table that matches the provider and provider_uid combination. If there is a match, then you can log the corresponding user in.

2 likes
bigblueboss's avatar

@khoanguyenme

I have the following code:

use App\User; use Auth;

class ManualAuthController extends Controller {

public function index()
{
    return view('fb');
}

public function redirectToProvider()
{
    return \Socialize::with('facebook')->redirect();
}

public function handleProviderCallback()
{
    $user = \Socialize::with('facebook')->user();

    if(User::where('email', '=', $user->email)){
        echo "Auth attempt matched";
        $checkUser = User::where('email', '=', $user->email);
        Auth::login($checkUser);
    }else{
        echo "auth no attempt matched";
        $row = new User;
        $row->email = $user->email;
        $row->avatar = $user->avatar;
        $row->name = $user->name;
        $row->save();
    }
    echo Auth::user()->name;
}

}

I get the following error: Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Database\Eloquent\Builder given

First and foremost, am I approaching this in the wrong way?

bigblueboss's avatar

I tried implementing Authenticatable, in which I get a new error saying: Class App\Http\Controllers\ManualAuthController contains 5 abstract methods and must therefore be declared abstract or implement the remaining methods (Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifier, Illuminate\Contracts\Auth\Authenticatable::getAuthPassword, Illuminate\Contracts\Auth\Authenticatable::getRememberToken, ...)

guybarnahum's avatar

Sorry for the late reply. But maybe others would benefit from this. Regarding the error "Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Database\Eloquent\Builder given"

This is because you need to get the result for the SQL Builder object, so instead of : $checkUser = User::where('email', '=', $user->email); Try $checkUser = User::where('email', '=', $user->email)->first();

Right now you are passing the SQL Builder object, not the user object, to Auth::login.

guybarnahum's avatar

Sorry for the late reply. But maybe others would benefit from this. Regarding the error "Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Database\Eloquent\Builder given"

This is because you need to get the result for the SQL Builder object, so instead of : $checkUser = User::where('email', '=', $user->email); Try $checkUser = User::where('email', '=', $user->email)->first();

Right now you are passing the SQL Builder object, not the user object, to Auth::login.

haneez's avatar

Even I am waiting for a lesson dedicated to this. Basic Form filling registration, then you provide an option to connect with Facebook and Twitter maybe. I've implemented in so many ways but it has been buggy and wouldn't be awesome until we do the Jeffrey's way ;-)

jimmerioles's avatar

@martinbean @sitesense @khoanguyenme very neat approach, one thing that's bugging me is how to unify all social accounts to point to the same user, let's say a user used "Login with facebook" to register/login then later forgot and used "Login with Twitter". I am aiming to maintain the integrity of my user's identity that when they login they would still be the same user whatever provider they would use, you have any cool thoughts on this?

martinbean's avatar

@TheHamsterOfGod Have a table that contains users’ social profiles. It only needs three columns: user_id, provider (Facebook, Twitter etc), and uid, the user’s ID on that site.

Allow your user to add social profiles via their account, so that the next time they attempt to log in with one it knows which user its attached to and can log them straight in.

If a user tries to sign in with say, Twitter, but that Twitter account hasn’t been linked to a user on your site, then you’ll need to ask the user to log in with their existing account or create a new, and associate the Twitter profile with that account.

1 like
jekinney's avatar

Keep in mind twitter doesn't allow access to a users email address. So in my implementation if a user registers with twitter, after I get the other info on redirect back I show the user a form anyways to set a password, if twitter I show an email input to, which most of the time requirements state needs validation. Ie email a validation code. As stated through a many to one relationships I link a user account to social account.

1 like
jimmerioles's avatar

@jekinney i've thought about that flow too! but haven't decided yet. One thing that bugged me before is that it's somehow just the same with manually registering through forms but i think im gunna go for that now since i also really need some data too like address/location that providers didn't include by default and needs request, if only twitter includes email it would be less messy, thanks for the push! :)

jimmerioles's avatar

@jekinney the only problem now is my database schema, im not so sure about the relationships.

I have a feeling that i would need not just two tables(users 1-m social_accounts with one to many relationship), if i store every detail of every provider on social_accounts table it would create lots of null columns.

So im thinking that with @martinbean 's approach,

Have a table that contains users’ social profiles. It only needs three columns: `user_, `provid (Facebook, Twitter etc), and `u, the user’s ID on that site.

it can be used to have a polymorphic relations(but must be one-to-one) from social_accounts(with 3 columns: user_id, provider, uid) to tables facebook_accounts, twitter_accounts, etc(with specific columns per provider type).

Does this make sense? or am i way off?

jekinney's avatar

@TheHamsterOfGod

Here is my social table I generally use:


    public function up()
    {
        Schema::create('socials', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->enum('social', ['facebook', 'google']);
            $table->string('social_id', 120);
            $table->string('social_token', 120);
            $table->timestamps();
        });

        $table->foreign('user_id')->references('id')->on('users');

You could use a third table for "drivers" if you want to be more dynamic with backend admin stuff instead of an enum column. Either way this allows storing of the required unique info to link the accounts to the Social either through registration or profile update later. You can of course add fields as required if you want more information saved.

A lot of sites that require more info on registration simply don't use social for registering, some allow linking during but majority only allow linking through a profile after the user has registered. Obviously depends on the app and scope of your required needs.

1 like
martinbean's avatar

@TheHamsterOfGod You don’t want a table per provider. Just have one table, called social_profiles or similar. Your provider column can be a foreign key if needs be. That way, if I add Facebook to my account, then it would insert a record into the social_profiles table like this:

User::socialProfiles()->insert([
    'provider_id' => 1, // Imagine Facebook has an ID of 1 in your providers table
    'uid' => $facebook->getId(), // My Facebook profile ID
]);

This way users can add as many social profiles to their account as your site supports. And to ensure a user can only add one social profile per provider, add an unique constraint on the provider_id and user_id columns so each user can only have one combination of each.

Schema::create('social_profiles', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('user_id')->unsigned();
    $table->integer('provider_id')->unsigned();
    $table->string('uid');
    $table->timestamps();

    $table->unique(['user_id', 'provider_id']);
    $table->unique(['provider_id', 'uid']);

    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->foreign('provider_id')->references('id')->on('providers')->onDelete('cascade');
});

I’ve also added an additional unique constraint on the above so that provider ID and UID are an unique combination, as you don’t want two different users to assign the same, say Facebook, account to their account in your application.

2 likes
jimmerioles's avatar

@martinbean thanks! very informative.

In my case i might need to persist every details provided by every provider for future flexibility(i might send request to them, and submit my app, for more data/details) not just these:

// All Providers
$user->getId();
$user->getNickname();
$user->getName();
$user->getEmail();
$user->getAvatar();

and if i put every detail of every provider in social_profiles table, i think it would be abit messy and hard to sort things out, lots of nulls.

In this case, would it still be better to use your approach or create table per provider? or any better approach?

martinbean's avatar

@TheHamsterOfGod Why would you need to store all details from a provider? If you store the UID, you can retrieve details at any later date.

Also, I’m not sure who you are planning to send and submit your app. Who is “them”?

And why are you determined you have a table per provider? I’ve explicitly said twice now that’s not a good idea. It just isn’t scalable to maintain a table per provider. How do you list all of a user’s connected accounts if you have rows across multiple tables? Add a row per service in a social_profiles table, then you can list all of a user’s account by doing something simple like User::socialProfiles()->get().

sitesense's avatar

@TheHamsterOfGod @jekinney - actually Twitter has been able to provide email addresses since April 2015.

Just one caveat, you need to have your app whitelisted (which isn't difficult) - read this: https://dev.twitter.com/rest/reference/get/account/verify_credentials

Changes were made to The PHP League's 'oauth1-client' in May so any version of Socialite that has been updated since then will be able to retrieve a Twitter user's email address - provided you get your app whitelisted.

1 like
casey's avatar

@martinbean

Why would you need to store all details from a provider? If you store the UID, you can retrieve details at any later date.

I like your schema approach, but was thinking I should store the user's info (email, avatar etc.) when the callback is complete. Are you saying that Socialite can retrieve the user's information after the fact (using the token and ID)?

Forgive me if this is a dumb question. I'm still trying to grasp the way oAuth and social profiles work.

martinbean's avatar

@casey If a user already has an account on your website, then that User record will already have things like name, email, avatar URL etc right? So when the user authenticates with Facebook, you just want to check the email address matches any in your users database table and create a record in a social profiles table saying, “User with Facebook ID of X belongs to user in my database with the ID of Y.” You don’t need to store the name and whatnot that came back from Facebook as you already have that with your current user record.

The only time you would want to use the data from Facebook is if there isn’t a matching email address in your users table. In this case, you need to create a user, so you could create a user record with the name etc returned from Facebook and link the account in one go.

Next

Please or to participate in this conversation.