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

FrazeColder's avatar

Laravel Socialite register user with social account and force them to pick a username

I want to give users the opportunity to login/register via social login (like Facebook or Google). In order to do this I am using the package Socialite provided by Laravel.

I also give the users the opportunity to register by email and password. When doing so they have to choose a username as well. So far so good. Here is no problem because when the user gets created he has submitted a username, a email address and a password.

However, when doing this via social login the user has not chosen a username. I just get the name of the user from Google or Facebook but that's not what I want. I want the user to choose a username/nickname.

The problem is that the username is not nullable because I am using implicit binding on my username for the user model.

Right now I am bypassing this problem by putting the email, provider and provider_id I get from the social login in the user session. Whenever the user session contains those 3 parameters I show a form to the user where he has to choose a username. When submitting the form the user gets created by the username provided from the form and the email, provider and provider_id from the session.

Another possible solution is to make username nullable in the database and store the email, provider and provider_id right away when doing the social login and setting the username to null. This would solve the problem of having a session conflict but I would have a empty username in my database which would lead to many other problems as username is my key for the implicit route binding. I don't really favor this solution too but it is one possible solution.

My question to you is now what better way to do this is there? My favorite way would be to pass the email, provider and provider_id to the ProductController@index` as PHP variables because they cannot be manipulated! However, I haven't found any solution on how to do this except doing this via sessions.

Here is my sourcecode so far:

SocialLoginController

class SocialLoginController extends Controller
{

    public function redirect($provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    public function callback($provider)
    {
        $getInfo = Socialite::driver($provider)->user();

        $user = $this->userExists($getInfo);

        if(!$user){
            $user = array();
            $user['email'] = $getInfo->email;
            $user['provider'] = $provider;
            $user['provider_id'] = $getInfo->id;
            $user['avatar'] = $getInfo->avatar_original;

            Session::put('user', $user);
            return redirect()->to(route('home'));
        }

        auth()->login($user);

        return redirect()->to(route('product.index'));

    }

    function createUser(SocialRegisterUserRequest $request)
    {
        //TODO: Avatar
        //TODO: Username

        $userSession = Session::get('user');

        $user = User::create([
            'name' => $request->name,
            'email' => $userSession['email'],
            'email_verified_at' => date('Y-m-d H:i:s'),
            'provider' => $userSession['provider'],
            'provider_id' => $userSession['provider_id']
        ]);

        Session::flash('user');

        auth()->login($user);

        return redirect()->to(route('product.index'));
    }
}

web.php (routes)

Route::get('/auth/redirect/{provider}', 'SocialLoginController@redirect');

Route::get('/callback/{provider}', 'SocialLoginController@callback');

Route::post('/social/register', ['as' => 'social.register', 'uses' => 'SocialLoginController@createUser']);

Some explanation for you: When doing a social login the user calls the route SocialLoginController@redirect. This passes to request to the redirect function. When the user has approved the social login for my app on Facebook or Google he will be redirected to the callback function. Here he checks if the user already exists. If so, he gets logged in if not the data are put into the session. Now the form for choosing a username will be shown to the user. When he submits this form he will be routed to the createUser function which then finally creates the user.

Any ideas on how to do this in a better way? I am grateful for any kind of help!

Kind regards

0 likes
4 replies
FrazeColder's avatar

I do know that. However, I want the user to be able to set an own nickname because the nickname provided by Facebook or Google mostly like is the first and last name.

thewebartisan7's avatar

It's strange, I always get nickname from Google and Facebook..

However, I do already that in the way you want.

In callback function I have set in session the user token and provider name, so that I can use it after to connect user.


        // Prepare for second step registration

        // Store the token encrypted in session so we can retrieve socialUser data in next step
        session([
            'provider'  => encrypt($social->slug),
            'token'     => encrypt($socialUser->token)
        ]);

For security reason encrypt this data.

Then I also check if is OAuth 1.x type which has also a token secret:

        // Store token secret when provided (OAuth 1.x)
        if(! empty($socialUser->tokenSecret)) {
            session([
                'tokenSecret' => encrypt($socialUser->tokenSecret)
            ]);
        } else {
            // Can happen that tokenSecret exist from a previous request, so remove if exist
            if($request->session()->exists('tokenSecret')) {
                $request->session()->remove('tokenSecret');
            }
        }

Then I load another view called registration second step, where I show to users all data imported from social provider, and he can edit all this data, not only username, also password.


        $socialUserData = $this->userModel->mapSocialData($socialUser);
        $socialUserData['provider'] = $social->slug;
        $socialUserData['provider_label'] = $social->label;

        return view('auth.register-step2', compact('socialUserData'));

Second step registration that create users looks like:

if(! $request->session()->has('provider')) {
            return redirect(route('register'))->with('error', 'Invalid request.');
        }

        $dataFromRequest = $request->validated();
        $dataFromRequest['avatar'] = $request->input('avatar');

        try {

            // Retrieve and delete from session the provider and token
            $provider = decrypt($request->session()->pull('provider'));
            $token = decrypt($request->session()->pull('token'));

            // Get socialUser by provider and token / secret
            if($request->session()->exists('tokenSecret')) {
                $socialUser = $this->socialite->driver($provider)->userFromTokenAndSecret(
                    $token, decrypt($request->session()->pull('tokenSecret'))
                );
            } else {
                $socialUser = $this->socialite->driver($provider)->userFromToken($token);
            }

            // If we have no social info for some reason throw new SocialGetUserInfoException
            if (! $socialUser) {
                throw new SocialGetUserInfoException($social,
                    trans('messages.no_user_data', ['social' => $social->label])
                );
            }

            // Create new user, login and redirect
            $user = $this->createNewUser($this->userModel, $socialUser, $dataFromRequest);
            $this->login($user);

            return redirect(route(config('user.redirect_after_registration')))
                ->with('success', __('You have successfully connected with :provider', ['provider' => $social->label]));

        } catch (DecryptException $e) {

            $request->session()->remove('provider');
            $request->session()->remove('token');
            $request->session()->remove('tokenSecret');

            return redirect(route('register'))
                ->with('error', 'An error occurred. Please try again.');
        }
1 like

Please or to participate in this conversation.