Next-auth Google Auth & Firebase Adapter

When i try to use

 adapter: FirebaseAdapter(firestore)

From -> https://next-auth.js.org/adapters/firebase

After firebase v9 the docs at next-auth hasen't been updated.

i get this error: TypeError: collection is not a function

My Code:

Api keys, and client secret has been replaced with dots (...) to hide them.

    import NextAuth from "next-auth";
    import Providers from "next-auth/providers";
    import { FirebaseAdapter } from "@next-auth/firebase-adapter";
    import { getFirestore } from "firebase/firestore";
    import { initializeApp, getApps } from "firebase/app";
    import "firebase/firestore";
    
    const firebaseConfig = {
      apiKey: "...",
      authDomain: "...",
      projectId: "...",
      storageBucket: "...",
      messagingSenderId: "...",
      appId: "...",
      measurementId: "....",
    };
    
    const app = initializeApp(firebaseConfig);
    
    
    const firestore = getFirestore(app);
    
    
    const options = {
      providers: [
        Providers.Google({
          clientId:
            "...",
          clientSecret: "...",
         
        }),
      ],
    
      adapter: FirebaseAdapter(firestore),
    
      // database: process.env.MONGODB_URI,
    };
    
    export default (req, res) => {
      NextAuth(req, res, options);
    };

Upvotes: 1

Views: 3872

Answers (2)

CrackerKSR
CrackerKSR

Reputation: 1877

Instead of using Firebase Adapter we can use Firestore Adapter with minimal and easy configuration

@auth/firebase-adapter: ^2.7.3
next": ^14.2.5
next-auth": ^4.24.10

// api/auth/[...nextauth]/route.js

const db = getFirestore(); 

export const authOptions = {
  adapter: FirestoreAdapter(db),
  providers: [...],
}

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

here db is firestore from firebase-admin. Don't forget to initialize the firebase-admin app to use firestore.

This will handle user info on firestore by creating some collections like sessions, users, accounts etc.

Screenshot of firestore containing generated user info on sign up screenshot of firestore

Upvotes: 0

KarimHesham
KarimHesham

Reputation: 131

I managed to solve this same error using the following approach:

1- As firebase v9 is following a modular approach, pass destructured FirebaseClient object to the firebase adapter as follows

// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import GithubProvider from "next-auth/providers/github";
import { FirebaseAdapter } from "@next-auth/firebase-adapter";
import { db } from "../../../firebase";

import {
  collection,
  query,
  getDocs,
  where,
  limit,
  doc,
  getDoc,
  addDoc,
  updateDoc,
  deleteDoc,
  runTransaction,
} from "firebase/firestore";

export default NextAuth({
  providers: [
    // OAuth authentication providers
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    GithubProvider({
      clientId: "",
      clientSecret: "",
    }),
  ],

  adapter: FirebaseAdapter({
    db,
    collection,
    query,
    getDocs,
    where,
    limit,
    doc,
    getDoc,
    addDoc,
    updateDoc,
    deleteDoc,
    runTransaction,
  }),
});

2-You might then get an error that states "cannot use toDate method of undefined", If this occurred tap into node_modules/@next-auth/firebase-adapter/dist inside index.js file replace the exports.format object with this code which adds a "?" operator after value object

exports.format = {
    toFirestore(object) {
        const newObjectobject = {};
        for (const key in object) {
            const value = object[key];
            if (value === undefined)
                continue;
            newObjectobject[key] = value;
        }
        return newObjectobject;
    },
    fromFirestore(snapshot) {
        if (!snapshot.exists())
            return null;
        const newUser = { ...snapshot.data(), id: snapshot.id };
        for (const key in newUser) {
            const value = newUser[key];
            if (value?.toDate)
                newUser[key] = value.toDate();
            else
                newUser[key] = value;
        }
        return newUser;
    },
};

3- Last thing you have to do is inside package.json remove the "^" before the next-auth version to disable package update when you do npm install which will reset all code changes occured in step 2.

This solution assumes you have firebase.js file which should look as follows

import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";

const firebaseConfig = {
  apiKey: "your_api_key",
  authDomain: "your_auth_domain",
  projectId: "your_project_Id",
  storageBucket: "your_storage_bucket",
  messagingSenderId: "message_sender_Id",
  appId: "your_app_Id",
};

// Initialize Firebase
const app = getApps.length > 0 ? getApp() : initializeApp(firebaseConfig);

const db = getFirestore(app);
const storage = getStorage(app);

export { app, db, storage };

Hope this solves your issue.

Upvotes: 5

Related Questions