tl;dr: https://github.com/rodrigopv/nuxt-firebase-social-auth-demo
First, we set up a Nuxt.js project, in my case I used yarn create nuxt-app
as shown on Nuxt getting started page
Then we install @nuxtjs/firebase by following their getting started guide.
Be sure to add your firebase keys (that you get from the firebase console after registering your web app on the project) to nuxt.config.js
, along with the following parameters to activate the auth module:
nuxt.config.js:
modules: [
[
'@nuxtjs/firebase',
{
config: {
apiKey: 'FILL_THIS',
authDomain: 'FILL_THIS',
databaseURL: 'FILL_THIS',
projectId: 'FILL_THIS',
storageBucket: 'FILL_THIS',
messagingSenderId: 'FILL_THIS',
appId: 'FILL_THIS',
measurementId: 'FILL_THIS'
},
services: {
auth: {
initialize: {
onAuthStateChangedAction: 'onAuthStateChanged',
},
ssr: true,
},
}
}
]
],
So your file should look like this now:
To allow Nuxt to validate the logged user on server-side (so we don't break SSR), we must also activate a service worker:
yarn add --dev @nuxtjs/pwa
in nuxt.config.js add a 'pwa' key:
pwa: {
workbox: {
importScripts: ['/firebase-auth-sw.js'],
// by default the workbox module will not install the service worker in dev environment to avoid conflicts with HMR
// only set this true for testing and remember to always clear your browser cache in development
dev: process.env.NODE_ENV === 'development',
},
}
Now, we'll use the Vuex store to manage the authenticated user data:
create store/actions.js
export default {
async nuxtServerInit({ dispatch }, ctx) {
if (this.$fire.auth === null) {
throw 'nuxtServerInit Example not working - this.$fire.auth cannot be accessed.'
}
if (ctx.$fire.auth === null) {
throw 'nuxtServerInit Example not working - ctx.$fire.auth cannot be accessed.'
}
if (ctx.app.$fire.auth === null) {
throw 'nuxtServerInit Example not working - ctx.$fire.auth cannot be accessed.'
}
// INFO -> Nuxt-fire Objects can be accessed in nuxtServerInit action via this.$fire___, ctx.$fire___ and ctx.app.$fire___'
/** Get the VERIFIED authUser from the server */
if (ctx.res && ctx.res.locals && ctx.res.locals.user) {
const { allClaims: claims, ...authUser } = ctx.res.locals.user
console.info(
'Auth User verified on server-side. User: ',
authUser,
'Claims:',
claims
)
await dispatch('onAuthStateChanged', {
authUser,
claims,
})
}
},
onAuthStateChanged({ commit }, { authUser, claims }) {
if (!authUser) {
commit('RESET_STORE')
return
}
console.log('AuthStateChangedAction', authUser)
commit('SET_AUTH_USER', authUser)
},
}
create store/getters.js
export default {
isLoggedIn: (state) => {
try {
return state.authUser.id !== null
} catch {
return false
}
}
}
create store/index.js
export default {
onAuthStateChanged({ commit }, authUser ) {
if (!authUser) {
commit('RESET_STORE')
return
}
commit('SET_AUTH_USER', authUser)
},
}
create store/mutations.js
import initialState from './state'
export default {
RESET_STORE: (state) => {
Object.assign(state, initialState())
},
SET_AUTH_USER: (state, authUser) => {
state.authUser = {
uid: authUser.uid,
email: authUser.email
}
}
}
create store/state.js
export default () => ({
authUser: null
})
Now our Vuex store should be ready to tell other components whether a user is logged in or not.
Let's create some basic pages for our example app:
- Main page: anybody can access
- Login page
- User panel
Main page
Nothing fancy used Vuetify on the example project:
Login page
Same as the main page, but now with a sign in with Google button:
Now we add the important methods to actually make the button work:
On pages/login.vue:
First, implement the method that will do the login logic:
methods: {
async signInWithGoogle() {
var provider = new this.$fireModule.auth.GoogleAuthProvider();
// You can add or remove more scopes here provider.addScope('https://www.googleapis.com/auth/contacts.readonly');
let authData = await this.$fire.auth.signInWithPopup(provider)
this.$router.push('/profile')
}
}
Then we make sure we are calling this method on our button click event:
<v-btn @click="signInWithGoogle" color="primary" class="ma-2">Sign in with Google</v-btn>
And now our button should be working and opening a google sign in popup 🤩.
Now, we have to implement the /profile route that we specified above, which would be exclusive to users already logged in.
I just copied the main page but added a Vuex getter to retrieve the logged in user email:
To implement a log out function we add the following method to our profile view page:
methods: {
async logout() {
await this.$fire.auth.signOut()
this.$router.push('/login')
}
}
and we bind the button to use it:
<v-btn @click="logout">Logout</v-btn>
Now, to protect our page from being accessed without being logged in, we create a middleware:
in middleware/auth.js:
export default function ({ store, redirect }) {
if (!store.getters['isLoggedIn']) {
return redirect('/login')
}
}
And then we protect the user profile page by adding
export default {
middleware: 'auth',
to each page we want to make exclusively for logged in users.
What now
We just achieved a social login with Google. Firebase supports a lot of other social providers that might need some extra steps to be configured, but it works nice and easy.
About SSR
Server-side rendering stores Vuex server-side and pass it to the client every time the page is refreshed.
Being Firebase library designed for use at client-side, using more features such as vuexfire along SSR might be a major challenge that I haven't figured out yet.
What if I don't want to use SSR
This tutorial should work just fine, and you can omit the PWA / Service worker part.
Feedback
I'm no firebase expert and I'm just discovering more about how to use it along Nuxt. Feel free to make any comment or improvement to this guide.
Example repo
I've uploaded this example to github in case you want to clone the final result.
Github Repo: https://github.com/rodrigopv/nuxt-firebase-social-auth-demo
More info
Huge thanks to lupas (https://www.github.com/lupas/) for his work on @nuxtjs/firebase, along with the demo that has almost everything essential to know about how to use it: https://github.com/lupas/nuxt-firebase-demo
Top comments (5)
Thanks a lot Rodrigo for this great article! 👏👏👏
I added a link to this article to the links section of the nuxtjs/firebase documentation, hope that's alright with you.
Best regards and keep on Firenuxting! 🔥🔥🔥
Hey, i tried this, actually I've used this module before too but my problem is that when I'm trying to display the currentUser in the navbar, it actually displays after few seconds and i want to fetch the user details on server side, can you please help me with this!?
I can share the code if you wish,
Thanks 😊
Hi,
Without analyzing too much the situation, sounds like you might want to add a loading screen before displaying the whole layout (so you don't show components where data is not available yet).
This tutorial allow you to have the user data on server side, but won't allow you to do authentified calls to firebase (such as retrieving firestore documents on behalf of the user).
That pattern is still being developed I think as Firebase has certain limitations when it comes to using it on SSR in the same way the browser does it (imagine the server doing many calls to firebase with different users tokens from the same IP, would look like suspicious activity).
What it makes sense to me is that vuex should be hydrated using Firebase Admin API on server-side instead of using the end user methods.
What I've read until now is that server should only know logged in user data (like this tutorial shows) to do basic routing decisions (such as sending the user to log in or show them the user profile view), and all the rest should be done on client-side as firebase is designed for.
Let me know if this make sense to you!
I am using this with
target: "static"
and ssr in the auth service is not defined, the login works as well as the auth state is changed, but it won't redirect to the/profile
url. Any idea why?After sign In the redirection to profile doesn't work.
Edit: Nevermind it's set on the google api platform, waiting for ma uri to update. I have a mismatch uri redirection.