firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.86k stars 891 forks source link

Server App can't log in with session cookie #8403

Closed maxijonson closed 3 months ago

maxijonson commented 3 months ago

Operating System

MacOS Sonoma 14.5 (23F79)

Browser Version

Arc Version 1.53.1 (52180)

Firebase SDK Version

10.12.4 (Admin 12.3.0)

Firebase SDK Product:

Auth

Describe your project's tooling

NextJS with Firebase

Describe the problem

I'm using session cookies created with firebase-admin's auth.createSessionCookie(idToken) instead of passing a client-side auth.currentUser.getIdToken() to authenticate my users. When initializing a server app with the authIdToken equal to that session token, I get an auth/invalid-user-token error.

If I store the client-side auth.currentUser.getIdToken() as cookie and use that one instead, it works. However, I was under the impression that storing a session cookie is better because they aren't as short-lived as client-side tokens and they are revokable.

This issue does not occur when using emulators, however, I'm guessing this is due to it not using the same kind of jwts as a normal project.

Current Behavior

A JWT created from firebase-admin's auth.createSessionCookie cannot be used as authIdToken for initializeServerApp. The error is

FirebaseServerApp could not login user with provided authIdToken:  
    FirebaseError: Firebase: Error (auth/invalid-user-token).

Expected Behavior

A JWT created from firebase-admin's auth.createSessionCookie should be a valid authIdToken

Steps and code to reproduce issue

  1. Sign in to an account and make a POST request to a server endpoint to set a secure, http-only session cookie that can be read in subsequent requests.

    // client-side
    await signInWithEmailAndPassword(auth, "test@example.com", "abc123");
    await fetch("/api/auth/sign-in", {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    },
    body: JSON.stringify({ idToken: await auth.currentUser.getIdToken() }),
    });
    // /api/auth/sign-in
    const sessionCookie = await auth.createSessionCookie(req.body.idToken, { expiresIn: 60 * 60 * 24 * 5 * 1000 });
    cookies().set("__session", sessionCookie, { maxAge: expiresIn, httpOnly: true, secure: true }); 
    return new Response("OK")
  2. Extract the session cookie created from step 1 to authenticate the server app.

    // serverApp.ts (server-side only)
    const getServerApp = async () => {
    const sessionCookie = cookies().get("__session");
    
    // FirebaseServerApp could not login user with provided authIdToken:  
    //     FirebaseError: Firebase: Error (auth/invalid-user-token).
    const serverApp = initializeServerApp(firebaseConfig, { authIdToken: sessionCookie });
    
    const auth = getAuth(serverApp);
    await auth.authStateReady();
    // ...
    }
hsubox76 commented 3 months ago

It is intentional that the client side SDK does not accept and use session cookies, and that they are not interchangeable with idTokens. Session cookies can only be consumed by the Admin SDK, as they are meant to be used in a secure server environment. Although FirebaseServerApp should run in a server (SSR) environment, all the auth methods it enables are still shared with the client SDK, and we can't add sessionStorage usage to client methods.

For now, the recommended way to provide the idToken to intializeServerApp is to use a service worker: https://firebase.blog/posts/2024/05/firebase-serverapp-ssr/

We're thinking about any better options we might be able to offer in the long term, but this is what we recommend right now.

maxijonson commented 3 months ago

Thanks for your reply. I'll be looking into the article you've shared!

google-oss-bot commented 3 months ago

Hey @maxijonson. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!