nuxt-community / firebase-module

🔥 Easily integrate Firebase into your Nuxt project. 🔥
https://firebase.nuxtjs.org
MIT License
642 stars 98 forks source link

[SSR] Accessing res.locals.user always returns null at nuxtServerInit #134

Closed ErickPetru closed 4 years ago

ErickPetru commented 4 years ago

First, congratulations for this nice module with a bunch of options. But...

I'm facing serious problems with Firebase Auth and SSR. Even after following each part of the the SSR tutorial, my nuxtServerInit always has res.locals.user empty.

What I've done:

So, after all day struggling with it I'm giving up and asking for help! I can invite to take a look at my repo if needed, I just can't make it public right now.

lupas commented 4 years ago

Hi @ErickPetru Did you by chance only try your app in NODE_ENV=development so far? Could you try setting the workbox setting dev to true as suggested here and try again?

If that doesn't do the trick, I'm happy to look at your repo and see what's going on.

ErickPetru commented 4 years ago

Hi @lupas, really thanks for that super fast reply!

But unfortunately yes, I already set dev to true at the workbox config. And also I already published the project to a Firebase Cloud Function, so I would have worked if it was a local problem. But the problem happens both in development or production.

I just invited you as a collaborator in the repo, if you don't mind to take a look.

lupas commented 4 years ago

Hey @ErickPetru

Okay, just learned you're deploying your app through Firebase Functions. Referring to this issue https://github.com/nuxt-community/firebase-module/issues/123 (second point) there are problems with the Auth SSR functionality when deploying on Firebase Functions.

I personally never really deploy my Nuxt apps on Firebase Functions so I'm not an expert there. @mentAl-maZe was looking into this issue. @mentAl-maZe , did you get it to work?

I'll look into this as soon as possible, got your invite so I'll try try to find time to look at it this weekend, but can't promise.

lupas commented 4 years ago

https://stackoverflow.com/questions/55938705/service-worker-registration-fails-with-firebase-hosting-and-functions

The solution might go into this direction.

lupas commented 4 years ago

@ErickPetru Ok my bad, I thought you also only run the app locally through functions. But just tested it locally through with the nuxt server and experience the same issue - so in this case it does not seem to be a Functions issue.

But that's good then, I should be able to find the issue.. I'll keep you posted :)

lupas commented 4 years ago

@ErickPetru

I did a reinstall of node_modules after deleting package.lock.json and it works fine for me now on localhost. I'm running it with npm run serve where I set "serve": "nuxt". (Had to disable server in nuxt.config.js).

Screenshot 2020-03-28 at 02 08 16

Can you try if deleting package.lock.json and node_modules and doing a fresh install fixes your issues as well?

If it does not, can you try and see if it works for you with running the "nuxt" command? If that works for you but npm run dev doesn't, we at least know where the issue lies.

smakman commented 4 years ago

I'm experiencing similar issues after following the migration guide from V4 to V5. Seems to be working on my dev machine but not on my colleagues. Also not working on Firebase Hosting and Heroku. Not sure how to debug though.

lupas commented 4 years ago

@smakman Firebase Hosting is static hosting, SSR Authentication won't work there in any case. Heroku on the other hand should work and is successfully running for e.g. nuxt-fire-demo.

Since SSR Auth uses service-workers it is important to note that one has to reset cache on localhost to make sure other service workers from other potential apps are not messing/overwriting the workbox sw with which our service-worker is loaded. Also, at this point dev has to be set to true within the workbox settings in order to load the service workers on localhost. Furthermore, service workers don't work on older browsers or IE.

Do you get the sw.js properly loaded on your Heroku deployment? Can you check that in dev tools?

ErickPetru commented 4 years ago

Hi @lupas, thanks again, but I still wasn't able to get it working even in localhost.

Did I followed the same steps as you did?

So I'm unsure that its something related with the express server/index.js, since the default nuxt command also couldn't get it working. Looking deeper here I'm now wondering: should I have a sw.js file inside static folder? It's not there, even after uninstalling and reinstalling @nuxtjs/pwa.

Well, and about the deployment, I'm a little confused about your last answer to @smakman. I understand that Firebase Hosting is static, so no server-side code, of course. But since I'm using Firebase Functions for SSR, it's supposed to work there too, right?

lupas commented 4 years ago

@ErickPetru If your sw.js does not get generated, then that is clearly the reason why it's not working. I might have forgotten to mention one crucial step yesterday, it was rather late: It seems like you don't load the PWA module. Could you try to do the following?

modules: [
    '@nuxtjs/firebase',
    '@nuxtjs/pwa' // <- add this
  ],

Regarding Firebase Hosting: Sorry I was a bit confusing. When I talk about Firebase Hosting I usually mean the static hosting, but of course if combined with FB Functions, FB Hosting can be used to dynamically serve content.

So if you serve your Nuxt app via Functions, the Functions are basically your NodeJS server. So it depends on what exactly you do in your Firebase Function. But theoretically it should work. Important is that the sw.js file from /static folder finds its way into the /public folder of the webhosting, as far as I understand that might be the issue with such types of hosting.

smakman commented 4 years ago

@lupas sorry, I indeed meant hosting with a Firebase function. The sw.js file is being loaded:

image

lupas commented 4 years ago

@smakman Alright, sorry for the misunderstanding.

So I can see that the workbox sw.js is being loaded, however the firebase-auth.sw is not loaded within. This is how it should look like:

Screenshot 2020-03-28 at 15 57 48

Did you add importScripts to your pwa settings?

workbox: {
      importScripts: [
        // ...
        '/firebase-auth-sw.js'
      ],

Your sw.js also looks very different from the one being generated from @nuxtjs/pwa - do you happen to use another dependency that might create a sw.js file?

smakman commented 4 years ago

Thanks for the quick replies @lupas.

I have @nuxtjs/pwa in my dependencies and added as module in nuxt.config.js. And I have this in my nuxt.config.js:

pwa: {
  meta: false,
  icon: false,
  workbox: {
    importScripts: ['/firebase-auth-sw.js'],
    dev: true
  }
}

Not sure about any other dependency that might create a sw.js file.

lupas commented 4 years ago

@smakman Can you share the rest of your nuxt.config.js and check if you use the current version of nuxt/pwa? I'm not entirely sure why your sw.js looks the way it does, it seems to be generated from some other source.

smakman commented 4 years ago

Here it is:

module.exports = {
  mode: 'universal',

  /*
   ** Headers of the page
   */
  head: {
    title: 'lighttribe',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Lighttribe' }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },

  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },

  /*
   ** Global CSS
   */
  css: ['~/assets/stylesheets/main.scss'],

  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    { src: '~/plugins/datepicker.js', ssr: false },
    '~/plugins/vee-validate.js'
  ],

  /*
   ** Nuxt.js modules
   */
  modules: [
    '@nuxtjs/pwa',
    [
      '@nuxtjs/firebase',
      {
        config: {
          apiKey: 'XXXX',
          authDomain: 'XXXX',
          databaseURL: 'XXXX',
          projectId: 'XXXX',
          storageBucket: 'XXXX',
          messagingSenderId: 'XXXX',
          appId: 'XXXX',
          measurementId: 'XXXX'
        },
        services: {
          auth: {
            initialize: {
              onAuthStateChangedAction: 'onAuthStateChangedAction'
            },
            ssr: true
          },
          firestore: true,
          storage: true
        }
      }
    ],
    '@bazzite/nuxt-optimized-images',
    [
      'nuxt-fontawesome',
      {
        component: 'fa',
        imports: [
          {
            set: '@fortawesome/pro-regular-svg-icons',
            icons: ['far']
          },
          {
            set: '@fortawesome/pro-light-svg-icons',
            icons: ['fal']
          }
        ]
      }
    ],
    'vue-scrollto/nuxt'
  ],

  /*
   ** PWA
   */
  pwa: {
    meta: false,
    icon: false,
    workbox: {
      importScripts: ['/firebase-auth-sw.js'],
      dev: true
    }
  },

  /*
   ** Router
   */
  router: {
    middleware: 'auth'
  },

  /*
   ** Optimize images
   */
  optimizedImages: {
    optimizeImages: true
  },

  /*
   ** Build configuration
   */
  build: {
    extractCSS: true,
    transpile: ['vee-validate/dist/rules'],
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {
      // Run ESLint on save
      if (ctx.isDev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  }
}
smakman commented 4 years ago

@lupas sorry, it turns out I was still using nuxtjs/pwa version 2.6. The sw.js file now looks similar to yours. Will try to get it deployed tomorrow. Thanks!

lupas commented 4 years ago

@smakman Great, hope that will fix it :) Let me know if it works especially on your Firebase Functions deployment!

smakman commented 4 years ago

Will do!

ErickPetru commented 4 years ago

Hey @lupas, what a pathetic forgetting I had got! In fact my nuxt.config.js was missing modules [ '@nuxtjs/pwa' ] as you stated. With that, I was able to get the SSR auth working both for localhost and for Firebase Functions and now es.locals.user is nicelly! Hurray!

Unfortunately life is not a bed of roses... Now I'm getting an error "FirebaseError: Missing or insufficient permissions" whenever I try to get data with $fireStore:

async asyncData (context) {
  const establishmentTypes = []

  const snapshot = await context.app.$fireStore
    .collection('establishmentTypes')
    .orderBy('text', 'asc')
    .limit(50)
    .get()

  snapshot.forEach((item) => {
    establishmentTypes.push(item.data())
  })

  return { establishmentTypes }
}

I first thougth it had something to do with asyncData, but even using this.$fireStore inside a mounted() handler results in the same error on the client-side.

Is there any other step to allow Firestore work? Or is that not related with the same issue anymore?

lupas commented 4 years ago

@ErickPetru Horrayyy! :D Glad it works now.

Most likely not related to this module or this issue at all, but I can nevertheless help to find the solution:

  1. Are your security rules of Firestore correctly set to give read access to the collection if a user is authenticated?

  2. Go to Firebase -> Authentication -> Sign-in method -> scroll down -> Authorized Domains -> make sure "localhost" is set

Does any of that change something?

ErickPetru commented 4 years ago

Hi again, really thanks for all your help and patience, @lupas.

Well, it's not working yet...

  1. Yes, I have a very permissive security rule that just ensure authenticated users:
rules_version = '2'

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if request.auth != null
      allow write, update, delete: if false
    }
  }
}

I tested it on the Firebase Rules Tests Lab with success. I know it may takes some time to replicate, but I already applied that rule some hours ago.

  1. Yes, localhost is one of my Authorized Domains. Indeed, I'm also getting the same error when accessing the deployed Firebase Function version.

I'm feeling a little lost with it... But an additional important fact: I forgot to mention that I had to remove ssr/credential from nuxt.config.js, otherwise the res.locals.user = null errors happen.

So is the credential config mandatory for using $fireStore?

smakman commented 4 years ago

@lupas I got it working on Heroku! Running into some other deployment issues with Firebase right now but as long as you make sure you copy over the contents of the static folder into the public folder I think it should be working just fine. Thanks for your help!

lupas commented 4 years ago

So is the credential config mandatory for using $fireStore?

Yes, if you want to make authenticated calls from the server to Firestore you need to activate server-side login as mentioned here, including the credentials. Just be aware that this is an experimental feature, so we can't guarantee it works neatly in all cases. There's an issue here where we will try to improve that.

In your specific case I would ask yourself if there is a lot of benefit in the approach you are going. What exactly do you want to achieve?

SSR is arguably beneficial for SEO, and to some extent performance, but for a website where all routes are behind authentication it doesn't make that much sense anymore imho.

mentAl-maZe commented 4 years ago

Hi all, sry for the late response Yes i solved it by migrating the serverMiddleware into a plugin (see #136)

lupas commented 4 years ago

Released Version v5.0.5 which fixes ServerLogin issues on Firebase Functions and other custom server setups. If you guys still have issues please create a new specific issue to your problem.

Thanks @mentAl-maZe for the PR!

ErickPetru commented 4 years ago

Hey @lupas, I understood your point and for now I'm just using mode: 'spa' andfirabse.services.auth.ssr = falsein mynuxt.config.js`. Everything is working well that way.

But I'm really still interested in solving the problem when SSR is enabled. So, I already updated the package to version 5.0.5 and ensured that I followed the docs about server side Firebase client SDK login as you pointed. My srr config is:

ssr: {
  credential: '~/assets/serviceAccount.json',
  serverLogin: true
}

Unfortunately this keeps throwing the error:

Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google 
OAuth2 access token with the following error: "Error fetching access token: invalid_grant (Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.)". There are two likely causes: (1) your server time is not properly synced or (2) your certificate key file has been revoked. To solve (1), re-sync the time on your server. To solve (2), make sure the key ID for your key file is still present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If not, generate a new key file at https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.

I already tried regenerating the .json admin credentials file but the error persists.

lupas commented 4 years ago

@mentAl-maZe Did you experience the error @ErickPetru mention^s above? Any idea what could lead to that?

mentAl-maZe commented 4 years ago

@lupas Yupp... sry :confounded: And hadn't committed the fix... Created new pull request #138

mentAl-maZe commented 4 years ago

@lupas, @ErickPetru Oh... sry, thought you were referring to an empty res.locals.user The error mentioned by @ErickPetru is most likely caused by admin not finding the correct credentials file.

If your testing locally with services.auth.ssr.credential = true you have to set the env Variable GOOGLE_APPLICATION_CREDENTIALS to the absolute path to a service account credentials file.

Otherwise it should work if you provide the path to the property (services.auth.ssr.credential = '~/path/to/credentials/file.json), as you did... :thinking:

If your testing with firebase emulators:start or similar (firebase serve) you must set credential = true and set the env variable as firebase creates a virtual machine with a different file system (one in which the static file path will no longer be valid)

When deployed on firebase functions you can simply set credential = true as the GOOGLE_ACCOUNT_CREDENTIALS env variable is set on hosting (provided that you have created a service account key)

mentAl-maZe commented 4 years ago

@ErickPetru Are you using a custom token? Or are you using a different firebase config for development? (The credentials file has to correspond to the firebase config)

alexisagos commented 4 years ago

Hey @lupas, I'm encountering the same issue it seems, my res.locals are empty. I have tried everything in this thread. I'm running the app locally with "yarn dev". I tried "rm -rf node_modules" and also deleted "firebase-auth-sw.js" before running again. Am I missing anything obvious ?

Thanks for your great module !

modules: [
    // Doc: https://buefy.github.io/#/documentation
    'nuxt-buefy',
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    // Doc: https://github.com/nuxt-community/dotenv-module
    '@nuxtjs/dotenv',
    // Doc: https://nuxt-community.github.io/nuxt-i18n/
    'nuxt-i18n',
    // Doc: https://firebase.nuxtjs.org/
    '@nuxtjs/firebase',
    // Doc: https://pwa.nuxtjs.org/
    '@nuxtjs/pwa'
],
firebase: {
    config: {
        apiKey: 'XXX',
        authDomain: 'XXX',
        databaseURL: 'XXX',
        projectId: 'XXX',
        storageBucket: 'XXX,
        messagingSenderId: 'XXX',
        appId: 'XXX',
        measurementId: 'XXX'
    },
    services: {
        auth: {
            initialize: {
                onAuthStateChangedAction: 'onAuthStateChanged'
            },
            ssr: true
        }
    }
},
pwa: {
    meta: false,
    icon: false,
    workbox: {
        importScripts: ['/firebase-auth-sw.js'],
        dev: true
    }
},

Screenshot 2020-04-02 at 12 20 38

My nuxtjs/pwa version is ""@nuxtjs/pwa@^3.0.0-beta.20":

mentAl-maZe commented 4 years ago

Can you share the contents of the following files from the build folder please:

Thank you

alexisagos commented 4 years ago

Hey @mentAl-maZe , here are the files:

.nuxt/index.js (Plugins section)

/* Plugins */

import nuxt_plugin_workbox_334156d0 from 'nuxt_plugin_workbox_334156d0' // Source: ./workbox.js (mode: 'client')
import nuxt_plugin_main_1b3a202e from 'nuxt_plugin_main_1b3a202e' // Source: ./firebase-module/main.js (mode: 'all')
import nuxt_plugin_initAuth_373b26af from 'nuxt_plugin_initAuth_373b26af' // Source: ./firebase-module/initAuth.js (mode: 'client')
import nuxt_plugin_pluginrouting_4676d4dc from 'nuxt_plugin_pluginrouting_4676d4dc' // Source: ./nuxt-i18n/plugin.routing.js (mode: 'all')
import nuxt_plugin_pluginmain_42cc8f1d from 'nuxt_plugin_pluginmain_42cc8f1d' // Source: ./nuxt-i18n/plugin.main.js (mode: 'all')
import nuxt_plugin_axios_13e5beb4 from 'nuxt_plugin_axios_13e5beb4' // Source: ./axios.js (mode: 'all')
import nuxt_plugin_buefy_547683f6 from 'nuxt_plugin_buefy_547683f6' // Source: ./buefy.js (mode: 'all')
import nuxt_plugin_veevalidate_6e5ad03a from 'nuxt_plugin_veevalidate_6e5ad03a' // Source: ../plugins/vee-validate (mode: 'all')

I don't have a .nuxt/firebase-module/ssrAuth.js file, only initAuth.js and main.js

mentAl-maZe commented 4 years ago

Please make sure you have @nuxtjs/firebase version >=5.0.6 This version introduced a fix to mitigate a problem with the inclusion of ssrAuth.js

If you are indeed on version 5.0.6 Please open a new issue.

renbesson commented 4 years ago

I was stuck on this issue for a week until I decided to merge the demo (nuxt/firebase) project to my personal project. It's been working fine.

zernonia commented 4 years ago
ssr: {
  credential: '~/assets/serviceAccount.json',
  serverLogin: true
}

Unfortunately this keeps throwing the error:

Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google 
OAuth2 access token with the following error: "Error fetching access token: invalid_grant (Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.)". There are two likely causes: (1) your server time is not properly synced or (2) your certificate key file has been revoked. To solve (1), re-sync the time on your server. To solve (2), make sure the key ID for your key file is still present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If not, generate a new key file at https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.

I already tried regenerating the .json admin credentials file but the error persists.

Currently I'm facing the same issue here. I've tried using the .env approach as well, but all seems to throw back the error above.

wobsoriano commented 4 years ago

Missing or insufficient permissions

Do you have a solution for this? Apparently if you have a security rule, you will get insufficient permissions. Here's a sample rule to get insufficient permissions:

match /users/{userId} {   
      allow read, update: if request.auth.uid == userId;
      allow create: if request.auth.uid == userId;
}

but when you do allow read, write: if true;, the error is gone.