@nuxtjs/firebase social auth
Tags: nuxt, firebase, auth
Published: 2020-11-08 00:05:29

**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](https://nuxtjs.org/docs/2.x/get-started/installation)

![Nuxt project is ready](https://dev-to-uploads.s3.amazonaws.com/i/gozazttgcv3fbph62pjs.png)

Then we install @nuxtjs/firebase by following their [getting started guide](https://firebase.nuxtjs.org/guide/getting-started).

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:
```javascript
  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:
![Nuxt.config.js after setting up firebase module](https://dev-to-uploads.s3.amazonaws.com/i/7rbtprieonibqsub0s3j.png)

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:
```javascript
  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
```javascript
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
```javascript
export default {
  isLoggedIn: (state) => {
    try {
      return state.authUser.id !== null
    } catch {
      return false
    }
  }
}
```


> create store/index.js
```javascript
export default {
  onAuthStateChanged({ commit }, authUser ) {
    if (!authUser) {
      commit('RESET_STORE')
      return
    }
    commit('SET_AUTH_USER', authUser)
  },
}
```


> create store/mutations.js
```javascript
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
```javascript
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:
![Main Page view](https://dev-to-uploads.s3.amazonaws.com/i/49wtuuvw1dg6i06tpz0r.png)

## Login page
Same as the main page, but now with a sign in with Google button:
![Sign In page view](https://dev-to-uploads.s3.amazonaws.com/i/6xsje0ujim9sbb370v9v.png)

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:
```javascript
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:
```javascript
 <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:

![User profile view](https://dev-to-uploads.s3.amazonaws.com/i/cmieliimz0bmejck8u11.png)

To implement a log out function we add the following method to our profile view page:
```javascript
  methods: {
    async logout() {
      await this.$fire.auth.signOut()
      this.$router.push('/login')
    }
  }
```

and we bind the button to use it:
```javascript
<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:
```javascript
export default function ({ store, redirect }) {
  if (!store.getters['isLoggedIn']) {
    return redirect('/login')
  }
}
```

And then we protect the user profile page by adding
```javascript
 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