Next.js Discord

Discord Forum

how should I implement auth in server components?

Unanswered
Mountain Chickadee posted this in #help-forum
Open in Discord
Mountain ChickadeeOP
hey, for almost a week I have been trying to understand how should I implement my auth system securely, so that the user wont be able to modify his user data.
to store the user data, I have been using context. and I am using firebase auth for this project.
how can I get the user data witout him able to just modify it using dev tools? because the user data is stored on a client context, I can only retrieve it in a client component! I have tried to understand how it works for so long, but I cant find a solution! can you please help out?
thanks (:

65 Replies

Morelet’s Crocodile
checkout AuthJS, they have a firebase adapter https://authjs.dev/getting-started/adapters/firebase
@Morelet’s Crocodile checkout AuthJS, they have a firebase adapter https://authjs.dev/getting-started/adapters/firebase
Mountain ChickadeeOP
I have tried using next auth, which also has a firebase adapter but it does not work with firebase security rules for firestore
@Mountain Chickadee I have tried using next auth, which also has a firebase adapter but it does not work with firebase security rules for firestore
this is the case because authjs uses the firebase admin sdk, which has full control over everything. with this, you would need to implement the edit operation ontop of your own nextjs APIs rather than through firebase's client library
i haven't tried firebase yet but what i might do in this situation is to build my own APIs, do validations on the server (like checking whether this user has access to this data), then actually modifying the data on the firestore on the backend
@iyxan23 hmm, did you use the firebase client library for this?
Mountain ChickadeeOP
yes I did
@iyxan23 i haven't tried firebase yet but what i might do in this situation is to build my own APIs, do validations on the server (like checking whether this user has access to this data), then actually modifying the data on the firestore on the backend
Mountain ChickadeeOP
it seems so complicted omg, why would it be so hard to implement secure auth in next js with firebase? I feel like I am missing something
might be that firebase is intended to be used for client-only webapps lol
firebase is a BaaS so it's already a backend on itself
Mountain ChickadeeOP
but client only webapps are not secure? can the user modify the user data using devtools?
if i understnad it correctly, everything about a client component can be modified by the user, so the fetching of the user data should not be on the client side
you probably could modify some policy settings on firebase to prevent that from happening
@Mountain Chickadee if i understnad it correctly, everything about a client component can be modified by the user, so the fetching of the user data should not be on the client side
fetching user data should be alright, it's that you have to prevent users to modify data that it's not authorized to do so
@iyxan23 fetching user data should be alright, it's that you have to prevent users to modify data that it's not authorized to do so
Mountain ChickadeeOP
right, but fetching happens in an async function, which can only be on the server
i had tried firebase on android waayy back then, it was a breeze, but once you got to security it was a nightmare 😆
@Mountain Chickadee right, but fetching happens in an async function, which can only be on the server
async functions can also run on the client, its not server-specific
@Mountain Chickadee right, but fetching happens in an async function, which can only be on the server
Mountain ChickadeeOP
but you can not do it in a server component because you cant use context in server components
@iyxan23 async functions can also run on the client, its not server-specific
Mountain ChickadeeOP
really? I didnt know that actually
@iyxan23 i had tried firebase on android waayy back then, it was a breeze, but once you got to security it was a nightmare 😆
Mountain ChickadeeOP
lol yea 😂 I have been trying to solve it for over a week. it looks so simpleeeee, but I can not solve it
that's why i'd choose to write a whole backend that uses firebase admin sdk to validate and do stuff there instead
Mountain ChickadeeOP
then whats the point of the whole firebase sdks?
i wouldn't recommend using firebase for a real app, i only see it as some means of prototyping and just playing around cause it's just so easy to work with
@Mountain Chickadee then whats the point of the whole firebase sdks?
probably some static sites with no backend at all
@iyxan23 probably some static sites with no backend at all
Mountain ChickadeeOP
but the whole point of it is to use it without api or something and use firestore and auth
man I really dont know what to do
yeah its kinda annoying lol
do you really need to use firebase?
Mountain ChickadeeOP
lol like the whole structure of the project is all based on firebase. everything is ready and working except the auth part
I did manage to make auth work, but anyone could use devtools to modify the hooks, and change their data
anyway, thanks for trying to help, really appreacite it!
if someone else knows what I am doing wrong, please tell me 😦
could you share some of your auth code?
Mountain ChickadeeOP
omg the discord character limit is so annoying!
give me a second to format it
yes ofc!

this is AuthContext.tsx:
"use client";
//imports

interface AuthContextProps {
  user: FirebaseUser | null;
  googleSignIn: () => Promise<void>;
  logOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextProps | undefined>(undefined);

interface AuthContextProviderProps {
  children: ReactNode;
}

export const AuthContextProvider = ({ children }: AuthContextProviderProps) => {
  const [user, setUser] = useState<FirebaseUser | null>(null);

  const googleSignIn = async () => {
    try {
      const provider = new GoogleAuthProvider();
      const result = await signInWithPopup(auth, provider);
      setUser(result.user);
    } catch (error) {
      console.error("Error signing in with Google:", error);
    }
  };

  const logOut = async () => {
    try {
      await signOut(auth);
      setUser(null);
    } catch (error) {
      console.error("Error signing out:", error);
    }
  };

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
      if (currentUser) {
        setUser(currentUser);

        const userExists = await getUser(currentUser.uid);
        if (!userExists) {
          await addUser(
            currentUser.uid,
            currentUser.email ?? "",
            currentUser.displayName ?? "",
            currentUser.photoURL ?? ""
          );
        }
      } else {
        setUser(null);
      }
    });

    return () => unsubscribe();
  }, []);

  return (
    <AuthContext.Provider value={{ user, googleSignIn, logOut }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error("useAuth must be used within an AuthContextProvider");
  }
  return context;
};
I need to get the user id, and then to fetch the data from my firestore database:
there are 2 options
option number 1:
"use client";
//imports

const AccountPage = () => {
  const { user } = useAuth();
  const [userData, setUserData] = useState<User | null>();

  console.log('userData', userData);
  useEffect(() => {
    const fetchData = async () => {
      try {
        if (!user) return;
        const userDataFromFunction = await getUser(user.uid);
        console.log("userDataFromFunction", userDataFromFunction);
        setUserData(userDataFromFunction);
      } catch (error) {
        console.error("Error fetching user data:", error);
      }
    };

    fetchData();
  }, []);


return 
//the reset of the component (uses the userData hook)

the problem with this approach is that all of the fetching happens in a client component, and the user is able to modify the data using devtools and change the hook.

option number 2:
"use server"
//imports

const AccountPage = async () => {
  const { user } = useAuth();

const fetchData = async () => {
  try {
    if (!user) return;
    const userDataFromFunction = await getUser(user.uid);
    console.log("userDataFromFunction", userDataFromFunction);
    return userDataFromFunction;
  } catch (error) {
    console.error("Error fetching user data:", error);
  }
};

const userData = await fetchData();

return 
//the reset of the component that uses the variable userData 

the problem with this approach is that context can't be used in a server components so this code won't run.
wdym by changing the hook anyway?
@iyxan23 wdym by changing the hook anyway?
Mountain ChickadeeOP
Using devtools, users can change the hook value
like you mean faking the user.uid?
tbh that's ok, as long as you protect the firestore from unauthorized operations
they have their own authorization method, like JWT or something, changing the user.uid wouldn't change their identity completely
@iyxan23 they have their own authorization method, like JWT or something, changing the `user.uid` wouldn't change their identity completely
Mountain ChickadeeOP
wow, I really didn't think about it. should I store it as a state?
and make everything client? because I cant use context in a server component
which way is recomended?
after login, you must have jwt token, right? @Mountain Chickadee
when you do data mutation, don't rely on user_id of request args but parse token to get user_id.
@James4u when you do data mutation, don't rely on user_id of request args but parse token to get user_id.
Mountain ChickadeeOP
you are right. what about protected routes? how sould I implement them?
sorry for the long time response, was abroad
there is no protected routes in next.js by default, you can use middleware to protect your routes
@James4u there is no protected routes in next.js by default, you can use middleware to protect your routes
Mountain ChickadeeOP
oh really? thats sounds weird, nextjs is the most used react framewrok and it doesn't have protercted routes?
there is no built-in authentication in next.js
you need to implement
Mountain ChickadeeOP
yes ofc, I am talking about protected routes with firebase and nextjs
you can create your middleware to protect your routes
Mountain ChickadeeOP
something like an api thing? isn't working with firebase should help not using api routes?
Masai Lion
@Mountain Chickadee Hi, How are you today?
Could you please explain in more details?
I am also currently using Firebase and NextJS.
So I think can help you.
@Masai Lion I am also currently using Firebase and NextJS. So I think can help you.
Mountain ChickadeeOP
thank you!
how did you set up your auth system?
Masai Lion
Just a sec. I will share it.
Mugger Crocodile
Hi @Mountain Chickadee
I can help you.
First, Did you store user data in Firebase Firestore/Realtime Database?
Mountain ChickadeeOP
Firestore
Mountain ChickadeeOP
so should I fetch the inforamtion from a hook and useEffect, making the component a client component?
@Masai Lion Just a sec. I will share it.
Mountain ChickadeeOP
still waiting for the code 🙂
I am trying to understand how should I store the useData in one context as a state, and then, other components can get the state and use it.
the problem is that every time the state changes, the component rerenders, and now I understand its not the best way to use userData, so I am looking for a good implemention of an auth system in nextjs