Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
GroophyLifefor opened this issue Mar 11, 2024 · 9 comments
Labels
adapters Changes related to the core code concerning database adapters bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@GroophyLifefor
Copy link

GroophyLifefor commented Mar 11, 2024

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 GroophyLifefor added adapters Changes related to the core code concerning database adapters bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Mar 11, 2024
@erenkulaksiz
Copy link

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

@GroophyLifefor
Copy link
Author

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
Copy link
Author

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

@erenkulaksiz
Copy link

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

@NickFoden
Copy link

NickFoden commented Apr 21, 2024

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.

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

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.

@GroophyLifefor
Copy link
Author

@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.

@FabioDainese
Copy link

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!

@Saiguna7
Copy link

Saiguna7 commented May 7, 2024

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
Copy link

Saiguna7 commented May 7, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
adapters Changes related to the core code concerning database adapters bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests

5 participants