I’m trying to implement google login in my web app. I’m using Laravel Socialite package in the backend. The issue is, it works in SPA mode but it doesn’t work in SSR mode.
The flow of my google login:
- “googleLoginRedirect” method gets called on q-btn click.
- In response I get a google redirect url from api which I then simply visit using “window.location.href”
- Browser takes the user to google login page.
- User signs in using google credentials.
- Google then redirects the user to a callback url. (The callback url consists of an unique code in params, which can be used only once, for fetching user details)
- Using “googleLoginCallback” method I send the code from the url as a param to the backend api using axios.
- In the backend the socialite api fetches user details using that unique code, and creates new user if it doesn’t already exist.
- The api then returns a token.
- Set the token in the cookie.
- Log the user in to dashboard.
Observation:
- The above flow successfully works when running quasar app in SPA mode.
- In step-6 the axios successfully sends the code to the api, BUT while running in SSR mode, the socialite api responds with a “400 bad request: This authorization code has been used.”
I’m new to the concept of SSR, so this might be naive of me to say, but is the axios request getting called two times? Like second time from the node server? because the unique code sent by google can be used only once. Please any input on this is valuable to me.
// pages/login.vue
<template>
<q-btn @click="googleLoginRedirect" icon="lab la-google" />
</template>
<script>
export default {
methods: {
googleLoginRedirect () {
return new Promise((resolve, reject) => {
this.$axios.get('/api/login/google')
.then((response) => {
resolve(response)
if (response.data.url) {
window.location.href = response.data.url
}
})
.catch((error) => {
reject(error)
})
})
}
}
}
</script>
// pages/please-wait.vue
<template>
<p> Please wait while we are logging you with google </p>
</template>
<script>
export default {
methods: {
googleLoginCallback () {
return new Promise((resolve, reject) => {
this.$axios.get('/api/login/google/callback', { params: { code: this.$route.query.code } })
.then((response) => {
if (response.data.token) {
resolve(response)
this.$q.cookies.set('token', response.data.token)
this.$store.commit('user/setLoggedIn', true)
this.$router.push({ name: 'dashboard' })
this.$q.notify({ type: 'positive', message: 'Login Success' })
}
})
.catch((error) => {
reject(error)
this.$router.push({ name: 'login' })
this.$q.notify({ type: 'negative', message: 'Failed to login for some reason' })
})
})
}
},
created () {
this.googleLoginCallback()
}
}
</script>
Here is how my axios boot file looks
// boot/axios.js
import Vue from 'vue'
import axios from 'axios'
import { Cookies } from 'quasar'
Vue.mixin({
beforeCreate () {
const options = this.$options
if (options.axios) {
this.$axios = options.axios
} else if (options.parent) {
this.$axios = options.parent.$axios
}
}
})
export default function ({ app, store, router, ssrContext }) {
const axiosInstance = axios.create({
baseURL: 'http://localhost:8000'
})
const cookies = process.env.SERVER
? Cookies.parseSSR(ssrContext)
: Cookies
axiosInstance.interceptors.request.use(function (config) {
const token = cookies.get('token')
if (token) {
config.headers.common.Authorization = `Bearer ${token}`
}
return config
}, function (error) {
return Promise.reject(error)
})
app.axios = axiosInstance
store.$axios = axiosInstance
router.$axios = axiosInstance
}