firebase-js-sdk
firebase-js-sdk copied to clipboard
Firebase v9 loads a large iframe.js file but only on mobile
[REQUIRED] Describe your environment
- Operating System version: macOS v11.3
- Browser version: Chrome 90.0.4430.212
- Firebase SDK version: [email protected]
- Firebase Product: auth
[REQUIRED] Describe the problem
After upgrading from v8 to v9 a request for an iframe (https://[projectname].firebaseapp.com/__/auth/iframe.js
) has started appearing on every page load, but only on mobile (via Chrome's simulated Device mode at least, and when Lighthouse is auditing the website).
An older Lighthouse report of our application, from before the upgrade to v9, did not mention this file at all so I can only assume it wasn't loaded back then.
This iframe file is big and seems unnecessary to our application since we never use any kind of iframing of Firebase functionality and the only auth login method we use is email login.
Is there a way to disable the loading of this iframe?
Hi @ludwigbacklund, thanks for the report. This iframe from Firebase Auth is used for third party login and eagerly loaded on mobile in certain situations. Currently, there’s no way to disable it. You may refer to this for your reference.
@looptheloop88 I see, thank you. However, I think you're a bit quick to close this. 263 kb of entirely unused code is a lot to download, parse and evaluate for nothing when our application has no use for it. I can imagine there are many other applications where this is true as well. For now we've had to use patch-package to manually disable the loading of this file which improved our Lighthouse score.
Hi @ludwigbacklund, thanks for the feedback. We are exploring for potential solutions, however we can't provide definite timelines or details. I have reopened this issue and added a feature request label for the meantime.
@ludwigbacklund Can you share the snippet of your solution?
@mark922 Open node_modules/@firebase/auth/dist/esm2017/index-somehash.js
. Find the _shouldInitProactively()
function. Change return _isMobileBrowser() || _isSafari() || _isIOS();
to return false
. Follow the patch-package README instructions to generate the patch and commit it.
Shouldn't this issue be given higher priority, since the benefit of firebase js package becoming smaller is nullified by this issue?
@ludwigbacklund there are multiple index-somehash.js files, which one should I take? Or all of them? Why are there multiple? (they all look the same)
@ludwigbacklund there are multiple index-somehash.js files, which one should I take? Or all of them? Why are there multiple? (they all look the same)
Search in the @firebase/auth node_modules directory for _shouldInitProactively
and you'll find it.
@looptheloop88 Could you please share if there's any update on this? If this issue is fixed, it will help us meet the core web vitals for an ecommerce site that we built for our client. As per pagespeed insights, if this is fixed, it will improve our LCP quite a bit (saw a very big improvement for desktop after moving to v9 but for mobile the score became worse).
Hi folks, the iframe code is in fact required if you're doing signInWithPassword
or signInWithRedirect
. If you're not using those methods (i.e. you're not using any of the OAuth providers), you can initialize Auth in a way that does not cause this code to load.
The default in getAuth()
pulls in all dependencies you might need, including browserPopupRedirectResolver
. You can tailor which dependencies are pulled in by using initializeAuth
instead of getAuth
.
The notes in the reference doc for initializeAuth
explain in more detail, but at a high level you can mimic the behavior of getAuth()
without pulling in the iframe code by using this code instead:
// initializeAuth throws an error if it's called more than once for the same app; save the reference.
const auth =initializeAuth(app, {
persistence: [indexedDBLocalPersistence, browserLocalPersistence]
});
You can verify this by looking at the code: https://github.com/firebase/firebase-js-sdk/blob/6564e99ec16612960c7ea49a368fc18197a5f2d1/packages-exp/auth-exp/index.ts#L137-L140
As you can see, the snippet I wrote earlier just omits the popupRedirectResolver
dependency. This will prevent the iframe code from loading. But remember, this will prevent you from using signInWithPopup
and signInWithRedirect
.
Hi @sam-gc, thank you for your response. Is it possible to delay the iframe code from loading until signInWithPopup
or signInWithRedirect
are called ?
In general it does delay. It only loads early in a few cases: https://github.com/firebase/firebase-js-sdk/blob/7028c1159b9153f14c22a81fdd09df68ffcfcfb6/packages-exp/auth-exp/src/platform_browser/popup_redirect.ts#L166-L169
@sam-gc Is there no way to fix this such that lighthouse doesn't penalize performance score for using firebase sdk?
Any updates on this? We'd definitely like to disable it on mobile until it's really needed.
Hi folks, it is indeed possible to delay loading that code until the sign in methods are called. The popup and redirect family of methods take an optional third parameter that is the popupRedirectResolver
. For example, the signature for signInWithPopup()
is as follows:
export declare function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise<UserCredential>;
So instead of using getAuth()
(or initializeAuth
with the popupRedirectResolver
set---these are the same), you can call initializeAuth()
without the popupRedirectResolver
option, then pass it in to the sign in functions as you need them. For example, this code below will always delay loading the iframe code until it is used:
import {initializeAuth, indexedDBLocalPersistence, browserLocalPersistence, browserSessionPersistence, browserPopupRedirectResolver, signInWithPopup, GoogleAuthProvider} from 'firebase/auth';
const auth = initializeAuth(app, {
persistence: [
indexedDBLocalPersistence,
browserLocalPersistence,
browserSessionPersistence
],
});
async function signIn() {
const result = await signInWithPopup(auth, new GoogleAuthProvider(), browserPopupRedirectResolver);
}
If anyone is looking for a patch. https://gist.github.com/mdathersajjad/628c53913c10f7e090d52871faf1c373. And add browserPopupRedirectResolver as given in above comment
Hi folks, it is indeed possible to delay loading that code until the sign in methods are called. The popup and redirect family of methods take an optional third parameter that is the
popupRedirectResolver
. For example, the signature forsignInWithPopup()
is as follows:export declare function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise<UserCredential>;
So instead of using
getAuth()
(orinitializeAuth
with thepopupRedirectResolver
set---these are the same), you can callinitializeAuth()
without thepopupRedirectResolver
option, then pass it in to the sign in functions as you need them. For example, this code below will always delay loading the iframe code until it is used:import {initializeAuth, indexedDBLocalPersistence, browserLocalPersistence, browserSessionPersistence, browserPopupRedirectResolver, signInWithPopup, GoogleAuthProvider} from 'firebase/auth'; const auth = initializeAuth(app, { persistence: [ indexedDBLocalPersistence, browserLocalPersistence, browserSessionPersistence ], }); async function signIn() { const result = await signInWithPopup(auth, new GoogleAuthProvider(), browserPopupRedirectResolver); }
The only problem is that you'll get a pop-up blocked error on the first try to login with a provider on Safari. If you try again it works because by than the iframe has loaded.
@abdo643-HULK yep, that's one of the reasons why the _shouldInitProactively
functionality exists
@abdo643-HULK yep, that's one of the reasons why the
_shouldInitProactively
functionality exists
Where can I find this function ? I am looking through the source code of the auth but can't seem to find it. And thnks for your help
It's this code here: https://github.com/firebase/firebase-js-sdk/blob/49b0406abb9b211c5b75325b0383539ac03358d1/packages/auth/src/platform_browser/popup_redirect.ts#L166-L169
By omitting the popupRedirectResolver
optional dependency in initializeAuth()
, that function is never called
Here goes my solution, i only dynamic import firebase auth, when I need it.
So I don't call it in the entire application, only when am making request to firebase.
If you have small to medium application, it should be to much work.
Tip* to keep the state of current user, use cookies
Using the examples from the comments, I get an error "TypeError: class constructors must be invoked with 'new'"
provideAuth(() => {
const auth: Auth = initializeAuth(getApp(), {
persistence: [
indexedDBLocalPersistence,
browserLocalPersistence,
browserSessionPersistence
],
popupRedirectResolver: undefined
});
if (environment.emulator) {
connectAuthEmulator(auth, 'http://localhost:9099', {disableWarnings: true});
}
return auth;
}),
const credential: UserCredential = await signInWithPopup(this.auth, new GoogleAuthProvider(), browserPopupRedirectResolver);
Ah, the problem lies within AngularFire. Will open a ticket there (https://github.com/angular/angularfire/issues/3038)
Another reason why this can be problematic is if you try to put the whole Auth module into a web worker. It works at first, until you test it on mobile, when suddenly it breaks because the web worker has no access to window.
The use of initializeAuth instead of getAuth (as outlined by sam-gc) fixed the issue for me.
// initializeAuth throws an error if it's called more than once for the same app; save the reference. const auth =initializeAuth(app, { persistence: [indexedDBLocalPersistence, browserLocalPersistence] });
PS. Having auth in a web worker already means that you cannot use signInWithPopup or Redirect, so that is not a drawback. To make that work for me using Google, I basically use Google Auth scripts to get a token, then send that token to my web worker auth script where I use signInWithCredential instead. :-)
I suspect this is what causes firebase to stop in PWA in offline mode. #5720
https://github.com/firebase/firebase-js-sdk/issues/4946#issuecomment-927800881
Is there nothing we could do to load iframe when we need ?
like after initialization and beforesignInWithPopup
@sam-gc Your solution works great, except for on Safari. I have tried different hacks to get the script to load when I actually need it (on sign in/sign up pages) but couldn't get anything to work reliably. A method to initialize the loading of the script would be perfect, so we don't have to load it on every page.
Pagespeed Insights even says that it is not gzipped. This could shave off some loading time and could improve this issue while we wait for a proper solution.
+1
If there's any way to get rid of the js, that would be sweet.
In the meantime, it should really get gzipped, it's a pretty bad hit on perf. The file is 263 kb unzipped while it ~could~ should be ~83 kb (~69% smaller). according to Pagespeed.
Hi all, thanks for the work to fix this. Are there any updates on the timeline? This is likely hurting SEO and load times for many users out there and gzipping would fix 69% of the issue, so would be a high ROI change to make when you get around to it.