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

pilat's avatar
Level 41

Gluing up Socialite and a stand-alone SPA

Hi, my project is consisting of two separate apps:

  1. Laravel-based back-end

  2. An SPA, "living" separately and built using Vue CLI.

Now, I'm trying to implement Login via Facebook.

There are two routes, on the backend:

Route::get('login/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('login/{provider}/callback', 'Auth\LoginController@handleProviderCallback');

So, if I click a link like: /login/facebook — I'll get logged into Laravel in the result.

Now, I need a way to pass user data to the SPA (which is NOT a part of Laravel). Here's what I've tried so far:

Laravel's Login controller (it's callback action):

// app/Http/Controllers/Auth/LoginController.php

   /**
     * Obtain the user information from Social provider.
     *
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback($provider)
    {
        $user = Socialite::driver($provider)
            ->fields([ 'id', 'name', 'email', 'picture', 'link', 'friends' ])
            ->user();

        session([ 'social_user' => $user ]);

        return redirect(config('app.spa_url') . '#authenticated');
    }

Then, there is such code in the SPA:

// src/pages/Home/Home.vue

  created () {
    this.$store.dispatch('loadData')

    if (location.hash.indexOf('authenticated') !== -1) {
      setTimeout(() => this.$store.dispatch('loadUser'), 1000)
    }
  },


// src/store.ts

  actions: {

    loadUser ({ commit, getters }) {
      let url = `${getters.backendUrl}/social_user`;

      axios.get(url).then(
        response => commit('DO_AUTH', response.data.user),
        error => // ...
      )
  }

Finally, here's the code for serving /social_user request in the Laravel:

// routes/web.php

Route::get('social_user', function () {
    return response()->json(session('social_user')->user);
});

Note: this is WEB.php, so, the session should be picked up. And it actually does, when I open /social_user URL in the browser. In the Ajax request, however, it's always empty?

So, I have those questions:

  1. Why the session variable is empty, but only if it's Ajax request?

  2. Are there any other ideas on how to pass user data from laravel to SPA?

One note in regards to #2: authorisation should be handled via redirects (just like Socialite does it). First, I've tried to implement "OAuth Implicit Flow", using popups — but this method didn't work well on mobiles. In particular, popup is not always (no in all browsers/OSes) closed after the access token is retrieved, so, the user thinks the authentication has failed.

0 likes
6 replies
pilat's avatar
Level 41

@BOBBYBOUWMANN - social_user is an object with some props (like, id, name, email) in its root, filled by Socialite driver (e.g., to ensure that certain props are always named the same, whatever provider is used) PLUS there is a 'raw' result of "get user info" request — this "raw result" is stored into 'social_user.user' and this is what I actually need in this place.

Concerning "everything in front-end": I've tried to use "OAuth Implicit" flow (browser only) for the authentication — this way I wouldn't need backend at all (at least for auth). But it didn't prove reliable, so, I decided to switch to "OAuth Authorisation Code" flow (browser + server-to-server) instead.

Anyway: I'm currently using cache (it could be database table just as well), but it's quite ugly: I pass cache key to the SPA via URL, then request user data, using this key from the URL…

Backend:

// app/Http/Controllers/Auth/LoginController.php

    public function handleProviderCallback($provider)
    {
        $methodName = Str::camel("get-{$provider}-user-data");

        /** @var array $user a structure of my own design =) */
    $user = $this->{$methodName}();

        $hash = md5("{$provider}.{$user['socialUserId']}");
        Cache::put("social_user.{$hash}", $user, now()->addHour());
        
        return redirect(config('app.spa_url') . "#{$hash}"); // config('app.spa_url') contains URL to the SPA 
    }
// routes/web.php

Route::get('social_user/{user_hash}', function ($userHash) {
    return response()->json(
        cache("social_user.{$userHash}")
    );
});

SPA:

// src/store.ts

  loadUser ({ commit, getters }) {
    const userHash = location.hash.substr(1, 32);
    const url = `${getters.backendUrl}/social_user/${userHash}`;

    axios.get(url).then(
      response => commit('DO_AUTH', response.data),
      error => // ...
    )
  }

What I don't like here — is that cache key needs to be in the URL when user in on SPA page.

pilat's avatar
Level 41

One idea: save user data under some unique "browser fingerprint" — the one, that can be collected by both backend (when handling authentication) and by SPA (when requesting user data).

This fingerprint should be:

  • the same for each consequent request within the same browser session

  • unique among the sessions (never repeated for another user)

  • (optional, but wanted) "collectable" from within PHP and JS code. I.e., Laravel gets request to '/login/facebook' — it can initiate the authentication procedure and then save the results under that "fingerprint" used as ID. Then, SPA wants to load user data, collected by Socialite: this is JS code, but it should be "able" to generated the same fingerprint for sending it to backend to deliver the user data.

Any ideas what can be used for this?

sheikh's avatar

@pilat hi can you please guide me how you login the user with facebook bacause i have a same scenario but i am

unable to implement facebook login

thank you

martinbean's avatar

@sheikh Dude. Don’t bump 2-year-old threads. If you have a question, create your own thread.

1 like

Please or to participate in this conversation.