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

Utilizing ng-model to control the visibility of a label or anchor tag

Here is the code snippet I am working with: <div class="tabs DeliveryRightDiv"> <label class="selected"><a>One</a></label> <label> <a>Two</a> </label> <label> ...

Using an array as the source for your Mui Autocomplete instead of JSON

I'm attempting to recreate the autocomplete MUI example shown here, but with a slight variation. Instead of utilizing a JSON data structure, I am simply passing an Array. Here is what I have tried: export default function SearchTutorialArray() { con ...

Is there a way to use JavaScript/jQuery to randomly resize images every time the page loads

Instead of generating thumbnails for my images, I am interested in displaying the original images on my page. However, I still need to resize these images for optimal viewing. Instead of applying a fixed width of 200px, I would like each image to be displa ...

Troubleshooting Error in Converting a JSX File with Tailwind CSS and Typescript

I found a code example on the Tailwind website. However, when I changed the file extension to .tsx, I encountered an error related to the className attribute. Do I need to specify a type for the className variable? What steps should I take to resolve thi ...

The VueRouter is unresponsive and not functioning as expected

I have been delving into Vue. Through the npm install vue-router command, I added vue-router to my project. Subsequently, I incorporated VueRouter and defined my URL paths within the VueRouter instances located in the main.js file. I created an About compo ...

Strapi x Next.js page with slug “/” is failing to display

Currently, I am in the process of developing a website using Strapi and Next.js. This project is based on the restaurant starter provided by strapi. However, I have come across an unusual problem while adding pages to the site... When I create a page with ...

Setting up and customizing Express with Angular

Currently working on a straightforward Angular/Express todo-list app, I encountered some difficulties. As the project is still in progress, after running the code on localhost:3000, I noticed that {{ thing }} was displayed literally on the webpage. The di ...

Using jQuery to send LocalStorage data to an email address with the help of PHP

Currently, I am in the process of developing a basic eCommerce/checkout system. My goal is to utilize localStorage data and transfer it to PHP using jQuery or any other method that is convenient. However, when I attempted to send the email, I only received ...

Customize Material-UI icons dynamically by changing their props in an array

I am looking to change props (color, size) for multiple icons in an array using Material-UI v4: const ICONS_ARRAY: React.ReactNode[] = [ <AlertCircleCheckOutline />, <AppleSafari />, <MotionPlay />, <AppleKeyboardCommand />, <Fil ...

Having difficulty modifying the values of my input types on the edit page

After successfully adding user values from the add user page, I encounter an issue when attempting to edit these values such as firstname and lastname on the edit page. Please review the code snippet below for any possible errors. import React from ' ...

Fetch the user's email address from Auth0 using an asynchronous function

I am trying to load the user's email from Auth0 and store it in a constant. However, I am getting an arrow. I cannot identify the error. Can anyone help me find a solution? const userEmailInfo = async () => { auth0Client = await auth.createClien ...

Issue: "TypeError: Unable to retrieve dynamically imported module" encountered while utilizing dynamic imports in Remix application

I am currently facing an issue with dynamic imports in my Remix application. Whenever I try to dynamically import a module using the code below: const module = await import(../lib/lang/lang-${language.toLowerCase()}.js); An error occurs: TypeError: Fail ...

Error in compiled.php on line 7772: Laravel throwing a RuntimeException when sending HTTP requests from Angular

Hey there, I've been encountering an error intermittently while using Angular $http methods with a Laravel API. Can someone please explain what this error means and suggest potential solutions? I've shared the error log on CodePen for easier refe ...

Choosing comparable choices from the drop-down menu

I am working on a dropdown menu that contains different options. I need to use jQuery to automatically select the option that corresponds to the branch of the currently logged in user. However, when some option text is very similar, it causes an issue. // ...

Tips for eliminating the undefined/null values from an array nested within another array in Angular

DATA = [{ application: [{ name: 'Room1' },{ name: 'Room2' },{ name: 'Room3' },{ name: 'Room4' },{ name: 'Room5' }], name: 'Batch 1&ap ...

Is it recommended to place the styles created by material-ui for child components after the styles generated for the parent component?

Utilizing material-ui, which utilizes JSS for styling a website. I have a component named Layout that applies margin to all its children (using the all children selector & > * in its style). This functionality works, but I would also like the child ...

A guide on extracting keywords from the tynt API using JavaScript

Looking to extract the inbound keyword from an API: What would be the best way to use JavaScript to extract and display the value of the inbound keyword from this API? ...

Tips on accessing close autoComplete/TextField title in AppBar

Looking to add a search bar and login button in the AppBar, where the search Bar is positioned close to the title. The desired order for the AppBar components should be as follows: Title SearchBox LoginButton How can this be achieved? Below is th ...

What is the process for establishing a reference to a property of an object in JavaScript?

Imagine you have an object structured like this: obj = {a:{aa:1}, b:2}; You decide to create a convenient variable (referred to as a pointer) named x that points to obj.a.aa with the following code: x = obj.a.aa; Next, your goal is to update the value ...

Ensuring a radio button is pre-selected by default in React by passing in a prop

Assume I have a React function similar to this function Stars({handleStarClick, starClicked}) { if (starClicked === 3) { document.getElementById('star3').checked = true } return ( <div className="rate"> ...