Next auth does not provide authentication functionality for Firebase

I've implemented next-auth with a firebase adapter, and while everything seems to be functioning properly in terms of saving users in the database, I'm encountering some issues with authentication.

import NextAuth from "next-auth"

import GoogleProvider from "next-auth/providers/google"
import { FirebaseAdapter } from "@next-auth/firebase-adapter"

import { db } from "../../../utils/firebase/firebase"
import * as firestoreFunctions from 'firebase/firestore'

import { adminAuth } from "../../../utils/firebase/firebaseAdmin"

import { getAuth, signInWithCustomToken } from "firebase/auth"

const auth = getAuth()

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
      state: false,
    }),
  ],
  adapter: FirebaseAdapter({
    db: db,
    ...firestoreFunctions,
  }),
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      console.log(user, 'user')
      const customToken = await adminAuth.createCustomToken(user.id)
      const customSignIn = await signInWithCustomToken(auth, customToken)
      console.log(customSignIn, 'customSignIn')
      console.log(customSignIn.user, 'customSignIn.user')
      user = customSignIn.user
      console.log(user, 'user 2')
      return true
    },
    async redirect({ url, baseUrl }) {
      return baseUrl
    },
    async session({ session, user, token }) {
      if (session?.user) {
        session.user.id = token.sub
      }
      return session
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      if (isNewUser) {
        const additionalClaims = {
          isStudent: true,
          isTeacher: false,
          isStaff: false,
          isAdmin: false
        }
        const customToken = await adminAuth.createCustomToken(token.sub, additionalClaims)
        const customSignIn = await signInWithCustomToken(auth, customToken)
        user = customSignIn.user

      }
      return token
    }

  },

  session: {
    strategy: 'jwt',
  },
})

Although my users are able to log in, they are not being authenticated.

const auth = getAuth()

onAuthStateChanged(auth, (user) => {
  if (user) {
    // User is signed in, see docs for a list of available properties
    // https://firebase.google.com/docs/reference/js/firebase.User
    console.log('user')
    console.log(user)
    // ...
  } else {
    console.log('no user')
    // User is signed out
    // ...
  }
})

Even though I am logged in, the Observer shows 'no user' as the result.

Answer №1

If you're not able to see a logged-in user, it could be because the authentication process is being handled on the server side rather than the client side.

One solution is to pass the customToken to the session and then utilize it on the client side to authenticate the user with firebase auth.

To streamline this process, consider wrapping the useSession hook in a custom hook as shown below and using that for authentication:

const useFirebaseSession = () => {
  const session = useSession();
  const [status, setStatus] = useState(session.status);

  useEffect(() => {
    if (session && session.status === 'authenticated') {
      signInWithCustomToken(auth, session.customToken).then(() => {
        setStatus('authenticated');
      });
    }
  }, [session]);

  useEffect(() => {
    if(session.status !== 'authenticated') {
      setStatus(session.status)
    }
  }, [session.status]);

  return { data: session.data, status };
}

Answer №2

Alright, here's how I tackled the problem: following @esi're advice, I passed the customToken created with firebase auth to the session:

async jwt({
  token,
  user,
  account,
  profile,
  isNewUser
}) {
  if (isNewUser || user) {
    const additionalClaims = {
      isStudent: true,
      isTeacher: false,
      isStaff: false,
      isAdmin: false
    }
    const customToken = await adminAuth.createCustomToken(token.sub, additionalClaims)
    console.log(customToken, '***customToken')
    token.customToken = customToken
  }
  return token
}

Next, within the session callback:

async session({
  session,
  token,
  user
}) {
  if (session?.user) {
    session.user.id = token.sub
    session.customToken = token.customToken
  }
  return session
},

Lastly, on the initial redirect page for users:

const [user, setUser] = useState()

useEffect(() => {

if (status === "authenticated") {
const loggedInUser = {
userName: session.user.name,
userEmail: session.user.email,
userPhoto: session.user.image,
userUID: session.user.id,
}

signInWithCustomToken(auth, session.customToken)

dispatch(setActiveUser(loggedInUser))

}
}, [status])

const handleLogOut = () => {
dispatch(setUserLogOutState())
logout()
signOut({
callbackUrl: '/'
}) //TODO: goodbye page


// Include remaining JSX code when session is true

And that's a wrap. Hopefully, this solution proves helpful to others as well.

Thank you!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to determine if any of the objects in an array contain a "completed" property with a value of false using JavaScript and React?

Is there a way to determine if at least one item in an array of objects has a completed property with a value of false using JavaScript and React? Here is an example array of objects: const items = [ { id: "32", jobs: [ ...

Steps to include a personalized function in a Mongoose Model

One way to extend Mongoose is by adding methods to documents. Here's an example: const userSchema = new mongoose.Schema({ balance: Number }) userSchema.methods.withdrawBalance = function(amount){ const doc = this doc.balance = doc.balance - amou ...

The Chartjs bar graph fails to display data upon initial page load

My HTML page includes a bar chart created using the ChartJS library. However, upon loading the page, the chart appears empty without displaying the data I provided and without rendering the bars. It only responds after clicking on the legend - first displa ...

Updating a React event as it changes with each onChange event

Let's address a disclaimer before diving into the issue - for a quick look, visit this pen and type something there. The Scenario This is the JSX code snippet used in my render method: <input value={this.state.value} onChange={this.handleCh ...

It appears that the NodeJs Express 4 async function in the model is returning before completion

I'm currently working on organizing my project by splitting the logic into different folders such as routes, views, models, and controllers. Within a model named data (models/datamodel.js), I have implemented two methods to retrieve data for populati ...

Update overall font size to be 62% for a consistent look across the website

Recently, I developed a browser extension that adds an overlay button to the LinkedIn website. Everything was running smoothly until I discovered that LinkedIn uses a global font-size: 62,5% that completely messes up my design.. https://i.stack.imgur.com ...

What is the best way to create a button that can cycle through various divs?

Suppose I want to create a scroll button that can navigate through multiple div elements. Here is an example code snippet: <div id="1"></div> <div id="2"></div> <div id="3"></div> <div id="4"></div> <div ...

The d3 hierarchy possesses the capability to compute the average values of child nodes

Looking for a solution with d3 visualization that involves averaging up the value of score on the lowest nodes and dynamically adding that average to the parent node above. It seems like there isn't an easy method in d3 for this task. The desired outc ...

Showing content from a JavaScript variable once a button has been clicked

Imagine you are using express.js and have a JavaScript variable embedded in an ejs file like this: <%= someVariable %> How can you display the value from this variable on the same page, for instance within a bootstrap modal element (check out https: ...

What is the correct way to position a material-ui icon within a button for proper alignment?

Is there a way to properly align the <ChevronRightIcon> within the <PrimaryButton>? I want it to appear after the button label on the right side. https://i.stack.imgur.com/dcEWo.png <PrimaryButton style={{ paddingRight: &apo ...

Transform asynchronous calls into synchronous calls

During my time building web applications in PHP, I was accustomed to handling tasks synchronously. Currently, my focus is on constructing a web scraper. The process involves: Obtaining a list of proxies Verifying the status of the proxies Scraping web c ...

What is the process of reading an excel file in angularjs?

I attempted to read an Excel file by following a tutorial I found at . Unfortunately, I encountered an undefined situation in the highlighted line below while trying to do so in IE11. var reader = new FileReader(); reader.onload = function( ...

Bootstrap 4 experiences issues with modal dialogs

I am experiencing an issue with my code not working on Bootstrap 4. When I click on the 'overview' button, the page darkens but the dialog does not appear. This functionality worked fine with the old version of Bootstrap. Can someone please assis ...

What causes the error message 'avoid pushing route with duplicate key' when using NavigationStateUtils in React Native?

Within my React Native + Redux project, I have set up a reducer for navigation utilizing NavigationStateUtils: import { PUSH_ROUTE, POP_ROUTE } from '../Constants/ActionTypes' import { NavigationExperimental } from 'react-native' impo ...

Different applications of data streaming apart from multimedia content

Exploring the various applications of streaming, particularly when sending data from a server to a visual client such as a web browser or an application, has sparked my curiosity. While I grasp the fundamental idea of transmitting data in chunks rather t ...

Jquery's ajax function is failing to execute the server side function

I have a specific structure for my solution: My goal is to execute the recommendationProcess function from CTL_RateRecommendationDetails.ascx.cs in CTL_RateRecommendationDetails.ascx Therefore, I wrote the following code: $.ajax({ type: "POST", ...

how to load CSS and JS files on certain views in Laravel 5.2

Currently, I am facing a situation where I need to ensure that the CSS and JS files are loaded only on specific views in Laravel 5.2. Due to my boss's decision to eliminate RequireJS for loading JS files on our blade templates, we are now exploring a ...

What methods can be used to control access to document.styleSheets data, and what is the purpose behind doing so?

Recently, I came across the document.styleSheets API, which allows you to access stylesheets used by a website. Using this API is simple, for example, document.styleSheets[0].cssRules will provide all CSS rules for the first stylesheet on a page. When I t ...

Checking the validity of email domains in real-time using Javascript

Is there a way to dynamically validate domains using JavaScript? I have a specific requirement which involves validating domains that are dynamically generated from user input. The goal is to match the domain with the company name selected and determine i ...

Encountering difficulties launching development server for Next.js 14 using the latest version of Yarn Berry

I'm encountering an issue when trying to run the development server in nextjs 14 using yarn. It works perfectly fine with pnpm and npm, but I am facing this problem only with yarn The error message: E:/Programming/TM/test/.yarn/__virtual__/next-virtu ...