I am trying to integrate social logins via laravel api backend using laravel socialite stateless option since I am building a single page app with vuejs and everything is done via http api calls.
However I am facing an issue with the social callback, current callback is being sent to laravel backend and it works, but now I want to return the user to the same page before starting the authentication, any idea on how to come around this ?
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->stateless()->user();
// dont know how to return the user to the last page on vue
}
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 handleProviderCallback(), assuming that you are using Passport API Authentication, you can try with this for the Controller:
public function handleProviderCallback($provider)
{
$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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With