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

Question: What is "getAuthenticatedAppForUser" for? #274

Open
mrvfino opened this issue Feb 17, 2024 · 9 comments · May be fixed by #307
Open

Question: What is "getAuthenticatedAppForUser" for? #274

mrvfino opened this issue Feb 17, 2024 · 9 comments · May be fixed by #307

Comments

@mrvfino
Copy link

mrvfino commented Feb 17, 2024

I've been using firebase on mobile for a long time already but kinda new to web. What's happening in "getAuthenticatedAppForUser" what purpose does it serve? I'm kinda confused because auth is client side right? Correct me if I'm wrong but outside what the codelab teaches this thing attempts to fetch the user from the server via the admin sdk, right?

@Herohtar
Copy link

From what I understand, it uses the Admin SDK to authenticate a user on the server side by verifying the token contained in the __session cookie passed from the client side via the header. I'm actually having some trouble getting this to work, as it seems Firebase Auth does not set that cookie. I'm not sure if it is something that changed in more recent versions of Firebase or something else...

@mrvfino
Copy link
Author

mrvfino commented Feb 27, 2024

@Herohtar thanks for the insight!

It looks kinda hacky for me and it feels like a gotcha because it's not in the codelab guide. So I didn't add it to what I'm working on.

@Herohtar
Copy link

I think the guide is out of date, and on top of that, I think the repo is missing some pieces. After doing a bit more digging, it looks like Firebase Auth doesn't, and never did, set a __session cookie, but rather, the client side part of the code needs to be storing the user's auth token in that cookie (or a similar one*) itself. The cookie then gets passed to the server actions via the request headers where it can be retrieved and used to authenticate Firebase on the server side of things.

* If you are using Firebase Hosting, it seems that the cookie has to be __session, as most other cookies are stripped from the request and __session is allowed through for exactly this kind of scenario..

@Rohit1024
Copy link

Yeah, very eager to know how this should be implemented

export async function getAuthenticatedAppForUser() {

I've similar requirement but using the next-firebase-auth-edge package for now

@rlw87
Copy link

rlw87 commented May 28, 2024

The guide that links to this repo implements this method by pulling the user's token out of a header set by the auth-service-worker. This does seem to work, but the issue I'm having is the service worker doesn't seem to see the newly logged in user (or logged out) at the same time as it changes in your client components.
This means you can't seem to reliably use any kind of guards on your server components such as

if (!user) {
    redirect('/login')
}

What I'm seeing is when a user logs in on my login page and the login page is listening for AuthStateChange events, when the auth state change changes to show the user is logged in, my component then sends the user to the user's account page, which has the guard in above. The issue is, when the service worker gets the current user to extract the token, the auth library tells it that nobody is logged in yet. Therefore when the user gets redirected to the accounts page, that page then immediately redirects them back to the login page. You can get around it by adding a sleep before redirecting to the account page, but this isn't reliable and doesn't always work.

I've been really happy with firebase for SPAs using React, but this example repo isn't making me want to use firebase for any Next.js apps.

@moriverse
Copy link

The guide that links to this repo implements this method by pulling the user's token out of a header set by the auth-service-worker. This does seem to work, but the issue I'm having is the service worker doesn't seem to see the newly logged in user (or logged out) at the same time as it changes in your client components. This means you can't seem to reliably use any kind of guards on your server components such as

if (!user) {
    redirect('/login')
}

What I'm seeing is when a user logs in on my login page and the login page is listening for AuthStateChange events, when the auth state change changes to show the user is logged in, my component then sends the user to the user's account page, which has the guard in above. The issue is, when the service worker gets the current user to extract the token, the auth library tells it that nobody is logged in yet. Therefore when the user gets redirected to the accounts page, that page then immediately redirects them back to the login page. You can get around it by adding a sleep before redirecting to the account page, but this isn't reliable and doesn't always work.

I've been really happy with firebase for SPAs using React, but this example repo isn't making me want to use firebase for any Next.js apps.

The guide that links to this repo implements this method by pulling the user's token out of a header set by the auth-service-worker. This does seem to work, but the issue I'm having is the service worker doesn't seem to see the newly logged in user (or logged out) at the same time as it changes in your client components. This means you can't seem to reliably use any kind of guards on your server components such as

if (!user) {
    redirect('/login')
}

What I'm seeing is when a user logs in on my login page and the login page is listening for AuthStateChange events, when the auth state change changes to show the user is logged in, my component then sends the user to the user's account page, which has the guard in above. The issue is, when the service worker gets the current user to extract the token, the auth library tells it that nobody is logged in yet. Therefore when the user gets redirected to the accounts page, that page then immediately redirects them back to the login page. You can get around it by adding a sleep before redirecting to the account page, but this isn't reliable and doesn't always work.

I've been really happy with firebase for SPAs using React, but this example repo isn't making me want to use firebase for any Next.js apps.

The guide that links to this repo implements this method by pulling the user's token out of a header set by the auth-service-worker. This does seem to work, but the issue I'm having is the service worker doesn't seem to see the newly logged in user (or logged out) at the same time as it changes in your client components. This means you can't seem to reliably use any kind of guards on your server components such as

if (!user) {
    redirect('/login')
}

What I'm seeing is when a user logs in on my login page and the login page is listening for AuthStateChange events, when the auth state change changes to show the user is logged in, my component then sends the user to the user's account page, which has the guard in above. The issue is, when the service worker gets the current user to extract the token, the auth library tells it that nobody is logged in yet. Therefore when the user gets redirected to the accounts page, that page then immediately redirects them back to the login page. You can get around it by adding a sleep before redirecting to the account page, but this isn't reliable and doesn't always work.

I've been really happy with firebase for SPAs using React, but this example repo isn't making me want to use firebase for any Next.js apps.

I have the exact same issue in the login scenario. Don't know if there is any way to solve this problem

@moriverse
Copy link

just figured that the following code can resolve the login redirect issue:

`const getAuthIdToken = async () => {
const app = initializeApp(config);
const auth = getAuth(app);

return new Promise((resolve, reject) => {
const unsubscribe = onIdTokenChanged(auth, async (user) => {
unsubscribe();

  resolve(user ? await getIdToken(user) : null);
});

});
};`

Looks like onAuthStateChange or authStateReady are not reliable across clients.

@IshfaqAhmedProg
Copy link

I think the guide is out of date, and on top of that, I think the repo is missing some pieces. After doing a bit more digging, it looks like Firebase Auth doesn't, and never did, set a __session cookie, but rather, the client side part of the code needs to be storing the user's auth token in that cookie (or a similar one*) itself. The cookie then gets passed to the server actions via the request headers where it can be retrieved and used to authenticate Firebase on the server side of things.

  • If you are using Firebase Hosting, it seems that the cookie has to be __session, as most other cookies are stripped from the request and __session is allowed through for exactly this kind of scenario..

Can you elaborate a little bit more? From what i understood i should use
Cookies.set("_session", await authUser.getIdToken(true))
on the client side custom hook! But i cant figure out on which useEffect it should go?

This one:

  useEffect(() => {
    const unsubscribe = onAuthStateChanged((authUser) => {
      setUser(authUser);
    });

    return () => unsubscribe();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

or this one:

useEffect(() => {
    onAuthStateChanged((authUser) => {
      if (user === undefined) return;

      // refresh when user changed to ease testing
      if (user?.email !== authUser?.email) {
        router.refresh();
      }
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [user]);

@jamesdaniels
Copy link
Member

Adding comments to getAuthenticatedAppForUser() which should help clarify things.

// Returns an authenticated client SDK instance for use in Server Side Rendering
// and Static Site Generation
export async function getAuthenticatedAppForUser() {
  // Firebase App Hosting currently does not support cookies, so we transmit
  // the client idToken via an Authorization header with Service Workers
  const authIdToken = headers().get("Authorization")?.split("Bearer ")[1];

  // Firebase Server App is a new feature in the JS SDK that allows you to
  // instantiate the SDK with credentials retrieved from the client & has
  // other afforances for use in server environments.
  const firebaseServerApp = initializeServerApp(firebaseConfig, {
    authIdToken
  });

  const auth = getAuth(firebaseServerApp);
  await auth.authStateReady();

  return { firebaseServerApp, currentUser: auth.currentUser };
}

Working on fixes to race conditions w/the service worker and such in #307. Hopefully we'll have cookie support soon and we can drop all this silliness, thanks for bearing with us.

@jamesdaniels jamesdaniels linked a pull request Sep 11, 2024 that will close this issue
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants