Next.js imported function errors out with 'ReferenceError' in getServerSideProps

Viewed 322

I am using Firebase for auth in my project. After user authenticates, I set his/her id token in cookies, so that next time any request is made to auth-only page, I can verify the token server-side for SSR.

However, the wrapper function I wrote for this errors out as 'ReferenceError' when used in getServerSideProps.

lib/firebase-admin.ts

import { initializeApp, App, AppOptions } from 'firebase-admin/app'
import { getAuth, Auth } from 'firebase-admin/auth'
import { credential } from 'firebase-admin'
import serviceAccount from '../secrets/firebase-admin-sdk.json'

// Firebase Admin app configs
const firebaseAdminConfig: AppOptions = {
    credential: credential.cert(JSON.stringify(serviceAccount))
}
// Get app admin instance and export it
const app: App = initializeApp(firebaseAdminConfig)
export default app

// Get auth admin and export
export const auth: Auth = getAuth(app)

utils/auth-server.ts

import { auth } from '../lib/firebase-admin'
import { DecodedIdToken } from 'firebase-admin/auth'
import AuthErrorMessages from '../constants/auth'

// Export function to verify id token in server side
interface IVerifyIdToken {
    status: boolean
    message?: string
    token?: DecodedIdToken
}

export const verifyIdToken = async (idToken: string): Promise<IVerifyIdToken> => {
    try {
        const decodedIdtoken = await auth.verifyIdToken(idToken, true)
        console.log(decodedIdtoken)
        return { status: true, token: decodedIdtoken }
    } catch (e) {
        return { status: false, message: e }
    }
}

components/test.tsx

import { GetServerSideProps, GetServerSidePropsContext, InferGetServerSidePropsType } from 'next'
import nookies from 'nookies'
import { verifyIdToken } from '../utils/auth-server'

export const getServerSideProps: GetServerSideProps = async (ctx: GetServerSidePropsContext) => {
    const cookies = nookies.get(ctx)
    if (cookies.token) {
        const idToken = await verifyIdToken(cookies.token) // ERROR HERE
        console.log(idToken)
        return {
            props: {
                email: 'DUMMY'
            }
        }
    } else {
        return {
            props: {
                email: "NO EMAIL (not logged in)"
            }
        }
    }
}

export default function Test({ email }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    return (
        <p>Your email: {email}</p>
    )
}

Error while opening /test

ReferenceError: Cannot access 'auth' before initialization
      at Module.auth (webpack-internal:///./lib/firebase-admin.ts:5:53)
      at verifyIdToken (webpack-internal:///./utils/auth-server.ts:12:87)
      at getServerSideProps (webpack-internal:///./pages/test.tsx:20:96)
      at Object.renderToHTML (/home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/render.js:479:26)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (internal/process/task_queues.js:97:5)
      at async doRender (/home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/next-server.js:1392:38)
      at async /home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/next-server.js:1487:28
      at async /home/captain-woof/Desktop/charity-cms/node_modules/next/dist/server/response-cache.js:63:36
1 Answers

I fixed the problem! (thanks @ArneHugo the hint)

So, what happened was not really a cyclic dependency, but files getting compiled asynchronously, because of which there was no actual control over what got compiled first.

I fixed this by making a small change:

lib/firebase-admin.ts

.
.
.
const serviceAccount = require('../secrets/firebase-admin-sdk.json') // Earlier -> import serviceAccount from '../secrets/firebase-admin-sdk.json'
.
.
.
credential: credential.cert(serviceAccount) // Earlier -> credential: credential.cert(JSON.stringify(serviceAccount))
.
.
.
// REPLACE ENTIRE BELOW PORTION WITH THIS
// Get app admin instance and export it
if (getApps().length === 0) { // To make sure only one instance is created and referred to at a time
    initializeApp(firebaseAdminConfig)
}

// Get auth admin and export
export const auth: Auth = getAuth(getApp()) // To make sure auth from only the one app instance we have is exported
Related