-
Notifications
You must be signed in to change notification settings - Fork 907
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
signInWithRedirect stops working following login from a second tab on Macs using Chrome #7037
Comments
I've been working on this bug so long that I developed a deep sadness inside but also a better understanding of what's going wrong. We're using the Microsoft OAuthProvider to handle authentication of our company's Azure AD tenant. I thought I fixed it a few hours after posting, but I see now that I was once again wrong; the app will eventually fall into a state where signing in no longer results in OAuth JWT data. As an tangental observation, I've noticed that when I click logout of outlook.com, I'm eventually presented with a message stating "" (you can see this message when navigating to their OAuth signout "endpoint". That's always come to me as a peculiar note for an application to leave for their end-users. Also, it seems like logging back into outlook.com in the same browser works fine anyway, so I've been safely ignoring the message... that is until this fateful week... So what I'm observing now is that, eventually the browser tab's state somehow becomes glitched! Clicking login and logout willy-nilly like a toddler that's just learned to click will function as expected, but eventually, it will stop working. I can consistently glitch my local tab's state ('localhost:3000') by:
const config = {
apiKey: AppConfig.VITE_API_KEY, // Same for all environments
authDomain: AppConfig.VITE_AUTH_DOMAIN, // Same for all environments
tenant: AppConfig.VITE_TENNANT, // Same for all environments
};
let app: FirebaseApp;
let auth: Auth;
async function initApp(): Promise<RedirectResult | null> {
console.debug('INIT APP WAS CALLED');
app = await initializeApp(config);
auth = await getAuth(app);
// This doesn't help us with auth bugs... opening a new tab always seems to fix
// the bug. I think it's Microsoft's OAuth implementation that is broken
// await auth.setPersistence(inMemoryPersistence);
// Check if we're local
// if (AppConfig.VITE_RPM_ENV === 'local-test')
// connectAuthEmulator(auth, 'http://localhost:9099');
// This must happen immediately after getAuth occurs
return await catchRedirectSignInMicrosoft();
}
async function signInMicrosoft(): Promise<void> {
console.debug('signInMicrosoft called');
const provider = new OAuthProvider('microsoft.com');
provider.setCustomParameters({
// prompt: 'login',
tenant: config.tenant
});
console.debug('calling signInWithRedirect');
signInWithRedirect(auth, provider).catch(error => console.log(error));
}
async function signOutMicrosoft(): Promise<void> {
console.debug('signOutMicrosoft called');
await auth.signOut();
// console.log('redirecting to MS logout screen because their OAuth seems to be broken.');
// window.location.href = 'https://login.windows.net/common/oauth2/logout';
}
async function catchRedirectSignInMicrosoft(): Promise<RedirectResult | null> {
console.debug('catchRedirectSignInMicrosoft was called.');
const result = await getRedirectResult(auth);
if (result == null) {
return null;
}
return {
user: result.user,
credential: OAuthProvider.credentialFromResult(result)
};
}
////////////////
// Init App
///////////////
initApp().then(result => {
if (result != null) {
console.debug('catchRedirectSignInMicrosoft: User appears to be logged in with Firebase Auth.');
const user = result.user;
const credential = result.credential;
let providerId = 'NO PROVIDER';
if (user.providerData[0]?.providerId != null) providerId = user.providerData[0]?.providerId;
console.log(`User logged in via ${providerId}`);
userContext.fillUserInfoFromRedirect(user, credential);
// Do app specific stuff following successful login
return;
}
console.debug('catchRedirectSignInMicrosoft: resulted in a null return object... we probably have that horrible bug again...');
if (checkIfWeAreInAPotentiallyStuckPendingState()) {
console.log('loginStatus was pending, but catchRedirectSignInMicrosoft response was null. Setting status to logged_out in case we\'re in a stuck state.');
console.log('DEBUG: Skipping removal of pending state to analyze bug');
userContext.setLoginStatus('logged_out');
return;
}
const user = getFirebaseUser();
if (checkIfAlreadyLoggedInViaSomePersistenceFromFirebase(user)) {
// Check to make sure we're using the absolute latest token before connecting.
user?.getIdToken()
.then((token: string) => {
userContext.setIdToken(token);
// Do app specific stuff following successful login
})
.catch((err: any) => console.log(err));
return;
}
if (checkIfOurLoginStatusIndicatesALoginEvenThoughFirebaseDisagrees()) {
console.warn('catchRedirectSignInMicrosoft: returned a null Firebase user, and userContext.loginStatus was logged_in. Logging out (possibly causing problems).');
userContext.logout();
}
}).catch(err => console.log(err)); |
It seems to be something going wrong with the response from the redirect iframe. I guess maybe there are more answers in Request URL: https://firebaseapp.com/__/auth/iframe.js. I think we'd need someone familiar with more of the iframe/ indexed DB end of things to get much further unless someone is able to diagnose why the iframe would stop working correctly when another tab logs into a separate domain sharing the same API key (there something in the indexDB persistence that I thought might be worth following up on, but I've never worked with that API). |
This sounds a little like #6914 where an app initialize would clear the session storage. But it is different because 1) I see the sample code waits for app initialization before issuing a redirect login 2) The session storage shouldn't be shared across tabs. Regarding:
I assume you added this log in the event handler? Can you also log authEvent.error? The UNKNOWN type is thrown if there was no entry for "redirectEvent::" key in session storage OR if web storage was unvailable in the browser. I assume it is the former. |
Sure, the console shows the authEvent including error details at the time of onAuthEvent being triggered: I'm surprised this bug isn't more expressed, I was beginning to think that it was a known bug related to browsers like Safari moving away from and you guys were holding while the situation developed. Following the article redirect best practices does fix the issue magically, so perhaps there aren't that many users depending on this code branch. Btw, here's what I'm seeing around getting session storage items, but I don't have any testing/ visibility for what the iframe is doing. Is its code open source and available to review on GH? I've never debugged iframes before, but I'd imagine there's a lead in that thing, wherever it's trying to read session storage (or validate it's session storage is available). |
Thanks for the additional details. It confirms that the auth event is not present when the iframe tries to read it. It is strange that this only happens when you have more than one tab open. But the redirect best practices you linked are the best way to keep signInWithRedirect working in all scenarios, glad to hear that it works in this case. The iframe code isn't open source unfortunately. It does a session storage read of a specific key and returns no-auth-event if it is empty. I am closing this since it works when the auth helper code is on the same domain, by following best practices. |
Thanks for these extra details and for your help supporting Firebase Auth. I'm not entirely convinced this is a closed issue however because it cost me a lot of effort sorting out what was going wrong and ultimately found that neither myself nor any available maintainers were able to fix the bug despite its remarkable reproducibility on multiple engineer's MacBook Pros. As an engineer adopting Firebase Auth, I would appreciate the redirect best practices article using much stronger, clarifying language to indicate that Using Alternatively, exposing the non-minified version of the OAuth helper scripts seems like a good idea too because then open source development and bug fixes would become a possibility bringing the necessary strength to the capability. |
Fair point.. I reopened this to track changing the best-practices doc to call out the requirements as you mentioned. |
We did update the docs in https://firebase.google.com/docs/auth/web/redirect-best-practices to mention "You must follow one of the options listed here for signInWithRedirect() to function as intended in production environments, across all browsers." Please reopen if you have more suggestions here. |
[REQUIRED] Describe your environment
Note: windows machines running chrome are strangely unaffected by this bug... Old Safari 15.6.1 seems to work fine, though 16.1+ reportedly breaks.
[REQUIRED] Describe the problem
Steps to reproduce:
SignInWithIdp
calls and thegetRedirectResult(auth)
always produces a null resultRelevant Code:
(See below post for complete contextual narrative and code).
The text was updated successfully, but these errors were encountered: