next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

My NextAuth auth system does not create users in firebase(Authentication as Firestore).

Open GroophyLifefor opened this issue 11 months ago • 16 comments

Adapter type

@auth/firebase-adapter

Environment

System:
OS: Windows 11 10.0.22621
CPU: (12) x64 11th Gen Intel(R) Core(TM) i5-11400H @ 2.70GHz
Memory: 1.04 GB / 7.75 GB
Binaries:
Node: 21.6.1 - C:\Program Files\
odejs\
ode.EXE
npm: 10.4.0 - C:\Program Files\
odejs\
pm.CMD
Browsers:
Edge: Chromium (122.0.2365.80)
Internet Explorer: 11.0.22621.1
npmPackages:
@auth/firebase-adapter: ^1.5.0 => 1.5.0
next: 14.1.3 => 14.1.3
next-auth: ^4.24.7 => 4.24.7
react: ^18 => 18.2.0

Reproduction URL

https://github.com/GroophyLifefor/reproduction

Describe the issue

Please READ HERE

Scenario: I am a developer, I wanted to use NextAuth, I tried to connect Firebase, "it was working on my computer" :D. I realized that it was not working in production, I tried to solve it in different ways but I could not solve it

First stage - traditional

the auth part of my project is divided into two parts.

/src/app/api/auth/[...nextauth]/route.ts  
/src/app/options.ts  
route.ts
import { authOptions } from '@/app/options';  
import NextAuth from 'next-auth';  
  
const handler = NextAuth(authOptions);  
  
export { handler as GET, handler as POST };  
  
options.ts
import { NextAuthOptions } from "next-auth";  
import GoogleProvider from "next-auth/providers/google";  
import FirebaseAdapter from "@next-auth/firebase-adapter";  
import { Adapter } from "next-auth/adapters";  
  
export const authOptions: NextAuthOptions = {  
  providers: [  
    GoogleProvider({  
      clientId: process.env.GOOGLE_CLIENT_ID as string,  
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,  
    }),  
  ],  
  adapter: FirebaseAdapter as Adapter,  
  secret: "http://127.0.0.1:3000/api/auth",  
};  

The problem I'm having here is, first of all, I get an error like this when compiling.

⚠ ./src/app/options.ts
Attempted import error: '@next-auth/firebase-adapter' does not contain a default export (imported as 'FirebaseAdapter').

This is like a warning but it says error in Vercel deployment and CI/CD stops.


and when I log in, it doesn't register in firebase.

image
image

But it's the only one I can log in, so it's a huge plus. that's is why this way first stage

Second Stage - Carriage

route.ts
import { authOptions } from '@/app/options';  
import NextAuth from 'next-auth';  
  
const handler = NextAuth(authOptions);  
  
export { handler as GET, handler as POST };  
  
options.ts - (M)odified
import { NextAuthOptions } from "next-auth";  
import GoogleProvider from "next-auth/providers/google";  
import { FirebaseAdapterConfig, FirestoreAdapter } from "@next-auth/firebase-adapter";  
import { Adapter } from "next-auth/adapters";  
import { connectToDatabaseNotAsync } from "@/lib/firestore";  
const { db } = connectToDatabaseNotAsync();  
import {  
  getFirestore,  
  collection,  
  query,  
  getDocs,  
  where,  
  limit,  
  doc,  
  getDoc,  
  addDoc,  
  updateDoc,  
  deleteDoc,  
} from "firebase/firestore";  
  
const firebaseClient = {  
  db,  
  collection,  
  query,  
  getDocs,  
  where,  
  limit,  
  doc,  
  getDoc,  
  addDoc,  
  updateDoc,  
  deleteDoc,  
};  
  
export const authOptions: NextAuthOptions = {  
  providers: [  
    GoogleProvider({  
      clientId: process.env.GOOGLE_CLIENT_ID as string,  
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,  
    }),  
  ],  
  adapter: FirestoreAdapter(firebaseClient as FirebaseAdapterConfig) as Adapter,  
  secret: "http://127.0.0.1:3000/api/auth",  
};  

At this stage I no longer get the import error in the build phase, but I get another error that runtime error.

error
bla bla  
  
'Cause'... 14538 more characters,  
name: 'Error'  
}  
[next-auth][error][SESSION_ERROR]  
https://next-auth.js.org/errors#session_error Unable to detect a Project Id in the current environment.  
To learn more about authentication and Google APIs, visit:  
https://cloud.google.com/docs/authentication/getting-started Error: Unable to detect a Project Id in the current environment.  
  
bla bla (very long)  
  
(THIS THREE TIMES COMES)  
at async Server.requestListener (C:\\Users\\Groop\\Desktop\\Desktop\\Code\\javascript\\commentinV2\\commentin\  
ode_modules\  
ext\\dist\\server\\lib\\start-server.js:140:13) {  
name: 'GetSessionAndUserError',  
code: undefined  
}  
[next-auth][error][SESSION_ERROR]  
https://next-auth.js.org/errors#session_error Unable to detect a Project Id in the current environment.  
  
bla bla  
  
at async Server.requestListener (C:\\Users\\Groop\\Desktop\\Desktop\\Code\\javascript\\commentinV2\\commentin\  
ode_modules\  
ext\\dist\\server\\lib\\start-server.js:140:13) {  
name: 'GetUserByAccountError',  
code: undefined  
}  

also auth too not working!

image

Third Stage - With over-research and deep found ways

route.ts
import { authOptions } from '@/app/options';  
import NextAuth from 'next-auth';  
  
const handler = NextAuth(authOptions);  
  
export { handler as GET, handler as POST };  
  
options.ts - (M)odified
import { FirestoreAdapter } from "@auth/firebase-adapter";  
import { cert } from "firebase-admin/app";  
import { NextAuthOptions } from "next-auth"  
import { Adapter } from "next-auth/adapters";  
import GoogleProvider from "next-auth/providers/google";  
import * as admin from 'firebase-admin'  
  
export const authOptions: NextAuthOptions = {  
  // Configure one or more authentication providers  
  // https://next-auth.js.org/providers  
  providers: [  
    GoogleProvider({  
      clientId: process.env.GOOGLE_ID as string,  
      clientSecret: process.env.GOOGLE_SECRET as string,  
    }),  
  ],  
  // see https://authjs.dev/reference/adapter/firebase#usage  
  // adapter: FirestoreAdapter({}),  
  adapter: FirestoreAdapter({  
    credential: cert({  
      projectId: process.env.FIREBASE_PROJECT_ID,  
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,  
      privateKey: process.env.FIREBASE_PRIVATE_KEY,  
    }),  
  }) as Adapter,  
  callbacks: {  
    async session({ session, token }) {  
      // ...  
      if (token && token.uid) {  
        const firebaseToken = await admin.auth().createCustomToken(token.uid as string)  
        // @ts-ignore  
        session.firebaseToken = firebaseToken  
      }  
      return session  
    },  
  },  
  session: {  
    strategy: 'jwt',  
  },  
}  

Previously I was only initializing firebase, I was doing it in a file called firestore.ts, I didn't feel the need to add it because there is nothing magical, it just initializes plain.

I imported firebase-admin as import * as admin from 'firebase-admin'
in this case take care as admin so It's not confict with firebase-app initialize

admin.initializeApp({  
    credential: admin.credential.cert({  
    projectId: process.env.FIREBASE_PROJECT_ID,  
    clientEmail: process.env.FIREBASE_CLIENT_EMAIL,  
    privateKey: process.env.FIREBASE_PRIVATE_KEY,  
  }),  
})  

and finally I have made some changes to my home page,

  • imported getAuth and signInWithCustomToken from "firebase/auth"
  • used getAuth to get auth
  • and a function called syncFirebaseAuth that manage signin and signout also called every session changed with useEffect

page.tsx

import { getAuth, signInWithCustomToken } from "firebase/auth";  
import { connectToDatabase } from "@/lib/firestore";  
import { FirebaseApp } from "firebase/app";  
  
export default function Home() {  
  const { data: session } = useSession();  
  const [auth, setAuth] = useState<FirebaseApp | null>(null);  
    
  async function initAuth() {  
    const { app } = await connectToDatabase();  
    // @ts-ignore  
    setAuth(getAuth(app));  
  }  
    
  async function syncFirebaseAuth() {  
      // @ts-ignore  
      if (session && session.firebaseToken) {  
    try {  
      // @ts-ignore  
      await signInWithCustomToken(auth, session.firebaseToken);  
    } catch (error) {  
      console.error("Error signing in with custom token:", error);  
    }  
    } else {  
      // @ts-ignore  
      auth.signOut();  
    }  
  }  
    
  useEffect(() => {D  
  syncFirebaseAuth();  
  }, [session]);  
    
  ...  
}  

also mine env was
image

so provider and adapter values was okey, but still error

error
bla bla  
  
[next-auth][error][adapter_error_getUserByAccount]  
https://next-auth.js.org/errors#adapter_error_getuserbyaccount Unable to detect a Project Id in the current environment.  
To learn more about authentication and Google APIs, visit:  
https://cloud.google.com/docs/authentication/getting-started {  
message: 'Unable to detect a Project Id in the current environment. \  
' +  
'To learn more about authentication and Google APIs, visit: \  
' +  
'https://cloud.google.com/docs/authentication/getting-started',  
stack: 'Error: Unable to detect a Project Id in the current environment. \  
' +  
'To learn more about authentication and Google APIs, visit: \  
' +  
'https://cloud.google.com/docs/authentication/getting-started\  
' +  
' at GoogleAuth.findAndCacheProjectId (C:\\\\Users\\\\Groop\\\\Desktop\\\\Desktop\\\\Code\\\\javascript\\\\commentinV2\\\\commentin\\\  
ode_modules\\\\google-auth-library\\\\build\\\\src\\\\auth\\\\googleauth.js:124:19)\  
' +  
' at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\  
' +  
  
bla bla (very long)  
  
'Cause'... 24114 more characters,  
name: 'Error'  
}  
[next-auth][error][OAUTH_CALLBACK_HANDLER_ERROR]  
  
bla bla  
  
2\\commentin\  
ode_modules\  
ext\\dist\\server\\lib\\router-server.js:377:13)  
at async Server.requestListener (C:\\Users\\Groop\\Desktop\\Desktop\\Code\\javascript\\commentinV2\\commentin\  
ode_modules\  
ext\\dist\\server\\lib\\start-server.js:140:13) {  
name: 'GetUserByAccountError',  
code: undefined  
}  

How to reproduce

Actually I couldn't get rid of these errors no matter what I did, if you try to setup it in a normal way you may get these errors.

Expected behavior

error-free, bug-free life

GroophyLifefor avatar Mar 11 '24 13:03 GroophyLifefor

Don't use NextAuth on App router. Atleast that is what i came to conclusion via my experience.

erenkulaksiz avatar Mar 11 '24 14:03 erenkulaksiz

Don't use NextAuth on App router. Atleast that is what i came to conclusion via my experience.

Is it fixing when I just don't use App Router? Really?

GroophyLifefor avatar Mar 12 '24 05:03 GroophyLifefor

Not even a return for how many days, great team, excellent project, The last praises I said is not serious.

GroophyLifefor avatar Mar 15 '24 16:03 GroophyLifefor

next-auth has 250 active issues and nearly 90 pr's. Just dont use next-auth @GroophyLifefor

erenkulaksiz avatar Mar 17 '24 08:03 erenkulaksiz

Hi GroophyLifefor,

(First > I am not a maintainer just am browsing the adapter implementations and saw your issue here)

I think you need to change how you import your env vars.

These packages are for 2 different environments here.

firebase more for the client side firebase-admin only for the server side

To use the client side library you have to follow next.js rules for environment variables.

  • https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables

And so for client side code you have to prepend NEXT_PUBLIC on all of your env vars.

So I expect your client side firebase app is not initiated / will not work when deployed

const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG || "");

-https://github.com/GroophyLifefor/reproduction/blob/5846491bbf84dc504f9eacb1412b3469826e2998/src/lib/firestore.ts#L12

Suggest for your client side file and firebase import /initialization to change to a config more similar to

const config = {
  apiKey: process.env.NEXT_PUBLIC_FB_API_KEY,
  appId: process.env.NEXT_PUBLIC_FB_APP_ID,
  authDomain: process.env.NEXT_PUBLIC_FB_AUTH_DOMAIN,
  databaseURL: process.env.NEXT_PUBLIC_FB_DATABASE_URL,
  projectId: process.env.NEXT_PUBLIC_FB_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FB_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FB_MESSAGING_SENDER_ID,
  measurementId: process.env.NEXT_PUBLIC_FB_MEASUREMENT_ID,
};

const app = initializeApp(config);

See the next.js repo for their examples and how they explicitly differentiate the client side firebase in its own file etc

  • https://github.com/vercel/next.js/blob/canary/examples/with-firebase/firebase/clientApp.js

And then I understand can be frustrating when coding, but also can consider this project is open source and lot of adapters and surface area to maintain. We can all try and be more considerate of people giving their valuable free time to help the web run smoothly.

If still stuck send me a message and I can help you. I use and have been using the firebase adapter with firebase-admin 11 in next.js projects and working well.

NickFoden avatar Apr 21 '24 18:04 NickFoden

@NickFoden Thank you for your reply and I remember trying this, unfortunately I can't make new attempts to fix because I have removed next-auth from the project and never to use this curse again.

I hope the project gets what it deserves, it's unbelievable that a package supported by Vercel is so bad

note: I use Google's apis and it's easier because at least it works.

GroophyLifefor avatar Apr 27 '24 17:04 GroophyLifefor

In case someone else if facing the same issue, in order to authenticate the user also on the Firebase Authentication dashboard, you just need to use createCustomToken and signInWithCustomToken Firebase functions.

For the longer answer, I'll leave you with this exhaustive example here on StackOverflow that I've recently posted (tbh it was related to the Firebase Stripe extension, but the main issue was exactly the one discussed here).

Hope this helps!

FabioDainese avatar Apr 28 '24 10:04 FabioDainese

create file name called next-auth.d.ts

import NextAuth,{DefaultSession} from "next-auth"
declare module 'next-auth'{
    interface Session{
        firebaseToken?:string;
user:{
    id:string;
}& DefaultSession["user"]
    }
}

create folder firebase/admin.ts

adapter for google or github authentications

import {initFirestore} from "@auth/firebase-adapter"
import admin from "firebase-admin"
let app;
if(!admin.apps.length){
    app=admin.initializeApp({
        credential:admin.credential.cert({
            projectId: process.env.FIREBASE_PROJECT_ID,
            clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
            privateKey: process.env.FIREBASE_PRIVATE_KEY,
        }),
    });
}
const adminDb=initFirestore({
    credential:admin.credential.cert({
        projectId: process.env.FIREBASE_PROJECT_ID,
        clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
        privateKey: process.env.FIREBASE_PRIVATE_KEY,
    }),
});
const adminAuth=admin.auth(app);
export{adminDb,adminAuth};

create a components/FirebaseAuthprovider.tsx this for google authentication for call backs and token created

"use client"
import { signInWithCustomToken } from 'firebase/auth';
import { useSession } from 'next-auth/react';
import React, { useEffect } from 'react'
import {auth} from "@/firebase/clientApp"
import { Session } from 'next-auth'
async function syncFirebaseAuth(session:Session){
    if(session&& session.firebaseToken){
        try{
            await signInWithCustomToken(auth,session.firebaseToken);
        }catch(error) {
            console.error("Error signing in with custom token:",error);
        }
    }else{
        auth.signOut()
    }
}
export default function FirebaseAuthProvider({
    children,
}: {
    children: React.ReactNode;
}) {
    const {data: session}=useSession();
   
    useEffect(()=>{
if(!session) return;
syncFirebaseAuth(session);
    },[session])
  return<> {children}</>
}

create a file in components/Sessionprovider.tsx

'use client';
import { SessionProvider} from 'next-auth/react';

type Props = {
  children: React.ReactNode;
}

export default function ClientProvider({children}: Props) {
  return (
    <SessionProvider>
      {children}
    </SessionProvider>
  )
}

after this rap this in layout.tsx

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import ClientProvider from "@/components/ClientProvider";
import FirebaseAuthProvider from "@/components/FirebaseAuthProvider";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
        <body className={inter.className}>
        <ClientProvider>
          {/* <FirebaseAuthProvider> */}
            {children}
            {/* </FirebaseAuthProvider> */}
   </ClientProvider>
        </body>
      </html>
  );
}

if your using google provider wrap that firebaseauthprovider it is not working in credentials provider because firebaseauth provider

use this in auth.ts

import CredentialsProvider from "next-auth/providers/credentials";
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from "@/firebase/clientApp";
import { NextAuthOptions } from "next-auth";
import { FirestoreAdapter } from "@auth/firebase-adapter";
import { adminAuth, adminDb } from "@/firebase/admin";
import Credentials from "next-auth/providers/credentials"
export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  pages: {
    signIn: '/sign-in'
  },
  session: {
    strategy: 'jwt',
  },
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {},
      async authorize(credentials): Promise<any> {
        try {
          const userCredential = await signInWithEmailAndPassword(auth, (credentials as any).email || '', (credentials as any).password || '');
          if (userCredential.user) {
            return userCredential.user;
          }
          return null;
        } catch (error) {
          console.error('Error during sign-in:', error);
          throw new Error('Authentication failed');
        }
      }
    })
  ],
  
  adapter: FirestoreAdapter(adminDb) as any,
  callbacks: {
    session: async ({ session, token }) => {
      if (session?.user) {
        if (token.sub) {
          session.user.id = token.sub;
          const firebaseToken = await adminAuth.createCustomToken(token.sub);
          session.firebaseToken = firebaseToken;
        }
      }
      return session;
    },
    jwt: async ({ user, token }) => {
      if (user) {
        token.sub = user.id;
      }
      return token;
    },
  },
} satisfies NextAuthOptions;

**now next auth is updated Simplify Server-side Authentication: Replace the various server-side authentication with a

methods ( getServerSession

function call in most cases.

getSession

withAuth

getToken

useSession

single

auth()**

Saiguna7 avatar May 07 '24 09:05 Saiguna7

In case someone else if facing the same issue, in order to authenticate the user also on the Firebase Authentication dashboard, you just need to use createCustomToken and signInWithCustomToken Firebase functions.

For the longer answer, I'll leave you with this exhaustive example here on StackOverflow that I've recently posted (tbh it was related to the Firebase Stripe extension, but the main issue was exactly the one discussed here).

Hope this helps!

how we can do with credentials provider ? how to store the data in database when sign up session callbacks

Saiguna7 avatar May 07 '24 09:05 Saiguna7

@GroophyLifefor did you find a solution here?

rpathways avatar May 28 '24 22:05 rpathways

@GroophyLifefor did you find a solution here?

Yeah, I found, I never use this .... again.

GroophyLifefor avatar May 29 '24 05:05 GroophyLifefor

@GroophyLifefor did you find a solution here?

Yeah, I found, I never use this .... again.

you can use clerk better and easly also i provided u the solve in above for next auth with google credentials , if u want custom sign in and sign up with next auth with firebase it is diffecult

Saiguna7 avatar Jun 06 '24 17:06 Saiguna7

@Saiguna7 I tried your solution, it still was not adding users to the authentication section of firebase.

On Thu, Jun 6, 2024 at 11:55 AM Saiguna7 @.***> wrote:

@GroophyLifefor https://github.com/GroophyLifefor did you find a solution here?

Yeah, I found, I never use this .... again.

you can use clerk better and easly also i provided u the solve in above for next auth with google credentials , if u want custom sign in and sign up with next auth with firebase it is diffecult

— Reply to this email directly, view it on GitHub https://github.com/nextauthjs/next-auth/issues/10280#issuecomment-2153094244, or unsubscribe https://github.com/notifications/unsubscribe-auth/AXDG4OYATT2AS2D6HM3JL2TZGCO75AVCNFSM6AAAAABEQKOZM6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNJTGA4TIMRUGQ . You are receiving this because you commented.Message ID: @.***>

rpathways avatar Jun 06 '24 17:06 rpathways

next-auth currently doesn't implement register because it assumes that you have existing users so it only provides signin and signout. as others mentioned if you want to add users to db you can implement that in a callback function or if you want to use firebase you can follow the official documentation here: https://firebase.google.com/codelabs/firebase-nextjs#0 which is way simpler than using next-auth. i would remove next-auth and stick with firebase

eminvergil avatar Jun 22 '24 19:06 eminvergil

@Saiguna7 I tried your solution, it still was not adding users to the authentication section of firebase. On Thu, Jun 6, 2024 at 11:55 AM Saiguna7 @.> wrote: @GroophyLifefor https://github.com/GroophyLifefor did you find a solution here? Yeah, I found, I never use this .... again. you can use clerk better and easly also i provided u the solve in above for next auth with google credentials , if u want custom sign in and sign up with next auth with firebase it is diffecult — Reply to this email directly, view it on GitHub <#10280 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AXDG4OYATT2AS2D6HM3JL2TZGCO75AVCNFSM6AAAAABEQKOZM6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNJTGA4TIMRUGQ . You are receiving this because you commented.Message ID: @.>

Remove credentials provider then it work u can see sonny songa from YouTube creating sass app i did from him https://www.youtube.com/live/OOUsvDOKlGs

Saiguna7 avatar Jun 22 '24 20:06 Saiguna7

Ya but firebase is complex and old for fetching data and role and session update all are complex now I am using supabase with prisma which is very very easy damm it saved a lot supabase is firebase alternative it is very good u need to try

Saiguna7 avatar Jun 22 '24 20:06 Saiguna7