colinhacks / next-firebase-ssr

An Next.js example repo for building authenticated pages with Firebase Authentication, cookies, and getServerSideProps
https://vriad.com/essays/nextjs-firebase-authentication
424 stars 61 forks source link

Guide seems to not work with NextJS 10 #16

Open AndreasJacobsen opened 3 years ago

AndreasJacobsen commented 3 years ago

This guide seems to no longer work with nextjs 10+.

Tested using code with my own specifications and copy paste from this repo. I also tested using both nookies and js-cookies

What happens: Token gets set correctly on login page, I can see the token in the applications panel of my chrome devtools. Whenever the token is used in SSR the token is shown as invalid by firebase and deleted from the front end cookie storage.

When trying to use this token on a SSR page the token dissapears, firebase gives an error stating

    code: 'auth/argument-error',
    message: 'First argument to verifyIdToken() must be a Firebase ID token string.'

My auth.tsx (at this point copy paste from this repo, tried replacing nookies with js-cookies but got the same issue)

//auth.tsx
const AuthContext = createContext<{ user: firebaseClient.User | null }>({
    user: null,
});

export function AuthProvider({ children }: any) {
    const [user, setUser] = useState<firebaseClient.User | null>(null);

    useEffect(() => {
        if (typeof window !== "undefined") {
            (window as any).nookies = nookies;
        }
        return firebaseClient.auth().onIdTokenChanged(async (user) => {
            console.log(`token changed!`);
            if (!user) {
                console.log(`no token found...`);
                setUser(null);
                nookies.destroy(null, "token");
                nookies.set(null, "token", "", { path: '/' });
                return;
            }

            console.log(`updating token...`);
            const token = await user.getIdToken();
            setUser(user);
            nookies.destroy(null, "token");
            nookies.set(null, "token", token, { path: '/' });
        });
    }, []);

    useEffect(() => {
        const handle = setInterval(async () => {
            console.log(`refreshing token...`);
            const user = firebaseClient.auth().currentUser;
            if (user) await user.getIdToken(true);
        }, 10 * 60 * 1000);
        return () => clearInterval(handle);
    }, []);

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

export const useAuth = () => {
    return useContext(AuthContext);
}
//authenticatedPage.tsx
export const getServerSideProps = async (req, ctx) => {
  try {
    const cookies = nookies.get(ctx);
    //Always fails here
    const token = await firebaseAdmin.auth().verifyIdToken(cookies.token);
    console.log("Token is:", token)

    return {
      props: {
        data: "Worked!"
      },
    };
  } catch (err) {
    const cookies = nookies.get(ctx);
    return {
      props: {
        data: "Did not worked!"
      },
    };
  }
};

const Revisions = ({ data, ctx }) => {
  const { user } = useAuth();
  return (
    <Layout>
      <Container>
        {/* always returns no user signed in */}
        <p>{`User ID: ${user ? user.uid : 'no user signed in'}`}</p>
        <p>test</p>
      </Container>
    </Layout>
  );
};

export default Revisions

Relevant versions:

    "firebase": "^8.2.10",
    "firebase-admin": "^9.5.0",
    "next": "10.2.3",
    "nookies": "^2.5.2",
    "react": "17.0.1",

What have I tried? Shifting from nookies to js-cookies Using the encode option with nookies

nookies.set(null, "token", token, { path: '/', encode: v => v  });

Edit: Tested with NextJS 11, same issue. It seems like the issue happens in auth.tsx when token is being unset if no user is present. The token gets unset when ran un an SSR page.

    return firebaseClient.auth().onIdTokenChanged(async (user) => {
      if (!user) {
        setUser(null);
        // Cookies.set("token", "Satt fra greia");
        // nookies.set(null, "token", "",);
        return;
      }

by commenting out the destruction of the cookie things begin working. But this seems to be like a very sub-optimal and possible insecure solution.

riordanpawley commented 3 years ago

@AndreasJacobsen any luck figuring out a more secure solution?

EyderACM commented 2 years ago

The issue comes from Firebase deleting cookies when using Firebase Functions or Cloud Run, as it only allows for using a special-named cookie called __session, try updating the name of the cookie from token to __session and it might do the trick.