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

Requested authentication state persistence isn't retained #8545

Open
dsl101 opened this issue Sep 30, 2024 · 3 comments
Open

Requested authentication state persistence isn't retained #8545

dsl101 opened this issue Sep 30, 2024 · 3 comments

Comments

@dsl101
Copy link

dsl101 commented Sep 30, 2024

Operating System

windows

Environment (if applicable)

Chrome 131, Firefox 130

Firebase SDK Version

10.13.2

Firebase SDK Product(s)

Auth

Project Tooling

React app / Vue & Quasar app

Detailed Problem Description

This page states that calls to setPersistence() prior to signInWithRedirect() should reapply the requested persistence model at the end of the redirect flow. We are seeing that despite requesting browserSessionPersistence before sending the link, and pasting the emailed link directly into the same browser tab, the persistence model is reverting to LOCAL.

This also happens with federated auth providers using signInWithRedirect().

Steps and code to reproduce issue

E.g. minimal piece of code to send a signin link by email:

  const signIn = (data) => {
    const options = {
      url: window.location.href,
      handleCodeInApp: true,     
    }
    console.log('options:', options)
    return setPersistence(auth, browserSessionPersistence).then(() => {
      console.log('auth:', auth)
      console.log('Persistence:', auth.persistenceManager.persistence)
      sendSignInLinkToEmail(auth, data.email, options).then(() => {
        console.log('sent link')
      })
    });
  };

Note the persistence printed to console here is:

useAuth.js:23 Persistence: BrowserSessionPersistence {type: 'SESSION', storageRetriever: ƒ}storageRetriever: () => window.sessionStoragetype: "SESSION"storage: (...)[[Prototype]]: BrowserPersistenceClass

After pasting the redirect link and re-entering the email address on the test app, this code detects & applies the authentication:

      if (isSignInWithEmailLink(auth, window.location.href)) {
        await signInWithEmailLink(auth, email, window.location.href)
        window.location.replace("/")  // Dismiss redirect
      } else {
        await signIn({ email });
      }

and the onAuthStateChanged() handler:

    return onAuthStateChanged(auth, async (userData) => {
      console.log("onAuthStateChanged:", userData);
      if (userData) {
        try {
          console.log("Persistence:", userData.auth.persistenceManager.persistence)
          setUser(() => userData);
        } catch (e) {
          // eslint-disable-next-line
          console.log(e);
        }
      } else {
        setUser(null);
      }

logs:

FirebaseAuthProvider.jsx:17 Persistence: IndexedDBLocalPersistence {type: 'LOCAL', _shouldAllowMigration: true, listeners: {…}, localCache: {…}, pollTimer: 1, …}
@dsl101 dsl101 added new A new issue that hasn't be categoirzed as question, bug or feature request question labels Sep 30, 2024
@google-oss-bot
Copy link
Contributor

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@jbalidiong jbalidiong added api: auth needs-attention and removed needs-triage new A new issue that hasn't be categoirzed as question, bug or feature request labels Sep 30, 2024
@dlarocque
Copy link
Contributor

Hi @dsl101, thanks for reporting this issue.

You mentioned that this issue also occurs when using signInWithRedirect- coud you share a snippet demonstrating how you used signInWithRedirect in this case?

As a temporary workaround while we look into this, would reapplying setPersistence(auth, browserSessionPersistence) after the redirect solve this issue?

@dsl101
Copy link
Author

dsl101 commented Oct 7, 2024

Calling setPersistence() after signInWithEmailLink() partially fixes the issue:

      if (isSignInWithEmailLink(auth, window.location.href)) {
        await signInWithEmailLink(auth, email, window.location.href)
        setPersistence(auth, browserSessionPersistence)
        window.location.replace("/")  // Dismiss redirect
      } else {
        await signIn({ email });
      }

Now, after sending the link and signing in on tab 1, opening a second tab and pasting in the URL does not show me as logged in on that second tab. Indeed, I can authenticate using a second email address on the second tab, and the 2 are isolated.

However, the react app has this section in App.js:

  if (user) {
    return (
      <div>
        <div>Logged in {user.email}</div>

        <br />

        <button onClick={() => window.open("/openedTab", "_blank")}>
          Open new tab same domain
        </button>
        <br />

        <button onClick={signOut}>Logout</button>
      </div>
    );
  }

Using that button on tab 1 (authenticated) opens a new tab and it is authenticated. Is this the expected behaviour when opening a tab via window.open()?

Regarding using signInWithRedirect(), the flow is almost identical, apart from not needing the detect the sign in URL on return to the application. The button is just <button onClick={() => signInRedirect() }>Login with google</button>, and the associated function:

import {
  browserSessionPersistence,
  setPersistence,
  signInWithRedirect,
  GoogleAuthProvider,
} from "firebase/auth"

  const signInRedirect = () => {
    return setPersistence(auth, browserSessionPersistence).then(() => {
      console.log('Persistence:', auth.persistenceManager.persistence)
      return signInWithRedirect(auth, new GoogleAuthProvider()).then(() => {
        console.log('redirect succeeded')
      })
    })
  }

Again, the logged persistence SESSION before the call to signInWithRedirect(), and LOCAL after returning.

Note that redirect succeeded is never logged, so I don't believe the promise is ever resolved back to the client code, I guess due to the redirect. So I'm not sure how the workaround can be used in this case, as it's not obvious how to detect the first auth flow vs just a refresh of a long-authenticated session.

However, and I don't know if this is a client-side bug or a browser setting, but the above flow for auth works fine (albeit with the persistence issue) on Firefox, but not on Chrome. The redirect for google authentication happens, and the app is reloaded at the end of that flow, but the onAuthStateChanged() handler always shows null, not the authenticated user. This is the react code for that:

  useEffect(() => {
    return onAuthStateChanged(auth, async (userData) => {
      console.log("onAuthStateChanged:", userData);
      if (userData) {
        try {
          console.log("Persistence:", userData.auth.persistenceManager.persistence)
          setUser(() => userData);
        } catch (e) {
          // eslint-disable-next-line
          console.log(e);
        }
      } else {
        setUser(null);
      }

      setFirstCheck(() => false);
    });
  }, []);

And the console logs for Chrome:
image

and Firefox:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants