Question: What is "getAuthenticatedAppForUser" for?
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?
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...
@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.
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..
Yeah, very eager to know how this should be implemented https://github.com/firebase/friendlyeats-web/blob/ddc901f79f19dc0968e3be35231a4d5e026f54bd/nextjs-end/src/lib/firebase/serverApp.js#L11
I've similar requirement but using the next-firebase-auth-edge package for now
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.
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
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.
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
__sessioncookie, 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__sessionis 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]);
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.