jdc
jdc

Reputation: 21

Laravel socialite + vue spa return access token to client

How can I return the access token that I'm getting in socialite callback to the client? I need to return this data back to vue for the user to be logged in. token data

This is my code in vue to get the redirect url

googleLogin(){
      const self = this;

      let socialLogin = {
          name: 'google'
      };

      self.$http.post(`api/googleLogin`, socialLogin)
          .then(response => {
              console.log('GOOGLE LOGIN RESPONSE ', response.body);
              window.location = response.body;
          })

  }

And for the backend

 public function googleLogin(Request $request){

    $socialType = $request->name;

    return response()->json(
        Socialite::driver($socialType)
            ->with(['social_type' => $socialType])
            ->stateless()
            ->redirect()
            ->getTargetUrl()
    );
}

public function googleLoginCallback(){

    $http = new \GuzzleHttp\Client;

    $user = Socialite::with('google')->stateless()->user();

    $userCredentials = [
        'token' => $user->token,
        'refreshToken' => $user->refreshToken,
        'expiresIn' => $user->expiresIn,
    ];


    $response = $http->post(env('LOGIN_ENDPOINT'), [
        'form_params' => [
            'grant_type' => 'social',
            'client_id' => env('CLIENT_ID'),
            'client_secret' => env('CLIENT_SECRET'),
            'network' => 'google',
            'access_token' => $userCredentials['token'],
        ]
    ]);
    return json_decode((string) $response->getBody(), true);
}

Upvotes: 1

Views: 3583

Answers (1)

asiermusa
asiermusa

Reputation: 190

I was working on something similar and I found a solution. The code is based in this great starter theme: https://github.com/cretueusebiu/laravel-vue-spa

--

In the googleLoginCallback(), assuming that you are using Passport API Authentication, you can try with this for the Controller:

public function googleCallback()
    {
        $provider = 'google';

        $user = Socialite::driver($provider)->stateless()->user();

        /* HERE CREATE USER WITH YOUR APP LOGIC. If email is unique... */

        // Login the created user
        Auth::login($user, true);

        // Get the username (or wathever you want to return in the JWT).
        $success['name'] = Auth::user()->name;
        // Create a new access_token for the session (Passport)
        $success['token'] = Auth::user()->createToken('MyApp')->accessToken;

        // Create new view (I use callback.blade.php), and send the token and the name.
        return view('callback', [
            'name' => $success['name'],
            'token' => $success['token'],
        ]);
    }

For the callback.blade.php view, the only thing you need is to send the requested token and username to the Vue app. For this you can use window.postMessage() method that allows to send data between windows, iframes...

<html>
<head>
  <meta charset="utf-8">
  <title>Callback</title>
  <script>
    window.opener.postMessage({ token: "{{ $token }}", name: "{{ $name }}" }, "YOUR DOMAIN");
    window.close();
  </script>
</head>
<body>
</body>
</html>

And finally this is my logic to the Login component in the vue app:

export default {
        // Waiting for the callback.blade.php message... (token and username).
        mounted () {
          window.addEventListener('message', this.onMessage, false)
        },

        beforeDestroy () {
          window.removeEventListener('message', this.onMessage)
        },

        methods : {
            // This method call the function to launch the popup and makes the request to the controller. 
            loginGoogle () {
              const newWindow = openWindow('', 'message')
              axios.post('api/login-google')
                    .then(response => {
                      newWindow.location.href = response.data;
                    })
                    .catch(function (error) {
                      console.error(error);
                    });
              },
              // This method save the new token and username
              onMessage (e) {
                if (e.origin !== window.origin || !e.data.token) {
                  return
                }
                localStorage.setItem('user',e.data.name)
                localStorage.setItem('jwt',e.data.token)

                this.$router.go('/board')
              }
        }
    }

    // The popup is launched.

    function openWindow (url, title, options = {}) {
      if (typeof url === 'object') {
        options = url
        url = ''
      }

      options = { url, title, width: 600, height: 720, ...options }

      const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screen.left
      const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screen.top
      const width = window.innerWidth || document.documentElement.clientWidth || window.screen.width
      const height = window.innerHeight || document.documentElement.clientHeight || window.screen.height

      options.left = ((width / 2) - (options.width / 2)) + dualScreenLeft
      options.top = ((height / 2) - (options.height / 2)) + dualScreenTop

      const optionsStr = Object.keys(options).reduce((acc, key) => {
        acc.push(`${key}=${options[key]}`)
        return acc
      }, []).join(',')

      const newWindow = window.open(url, title, optionsStr)

      if (window.focus) {
        newWindow.focus()
      }

      return newWindow
    }

</script> 

I hope it helps you!

Upvotes: 6

Related Questions