2

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
2
  • 1
    You may have a circular dependency, meaning that there is no order in which the files can be compiled that allows everything to be initialized before it is accessed. github.com/webpack/webpack/issues/12724 Commented Oct 31, 2021 at 21:34
  • @ArneHugo Thanks for pointing me in the right direction. I fixed the issue, and posted about it below. Commented Nov 1, 2021 at 3:08

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.