Reputation: 21
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
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