-
Notifications
You must be signed in to change notification settings - Fork 892
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
Database and Firestore throw away emulated auth credentials, if initialized first #4110
Comments
Here's a Stackoverflow Question with the same problem and a workaround: |
What if the intention is to use prod auth with emulated Firestore/Database, then using the emulated token in cache would be wrong. I don't think the SDK should be responsible for guessing what user wants to do, and user should make sure @samtstern once proposed to have an app level |
I'm not suggesting that we only take emulated credentials, but to not throw them away & log the user out if your pointing at the emulator w/o first initializing Auth. FWIW it seems storage and functions don't do this (last I checked) it's only Firestore & database. Say you (as a developer) have an app that heavily uses Firestore/Database on you main component but you only import Auth in components that require it (sign in, admin section, comments, etc.) To save the bytes on that critical first render (and because Auth is heavy) you code-split/lazy load it... or it's entirely a child component on a nested route (article -> comments) and is chunked automatically by your tooling. This isn't uncommon. Now you have a confusing experience when pointing at the emulator as you log out on every refresh or if using Hot reloading (say angular) everytime you save a file. It's not always easy to control order of operations when you're trying to be side effect free, limit module scope, & do code splitting. Further, SDK wise, I won't be able to add a work around for this in Angular/AngularFire without bumping a major. I'll only be able to at all because I'd have the Auth emulator settings in DI (global scope), could check for that, ensure the Auth chunk has been loaded, and then proceed with Firestore initialization. This would require AngularFire to have an entirely async API, which is the direction I'm steering it towards, but slowly. In ReactFire we'd never be able to build in a work around due to its design & would have to rely on docs to explain. |
It's actually Auth throwing out the emulated credentials. Auth is immediately initialized when it's dynamically loaded before AngularFire calls Storage and Functions work because they ask for auth token on demand( they don't register a listener), so auth is initialized later and we have the chance to call I think what we need is a flag in the global scope that sets the Auth emulator state, and is read by the Auth SDK on initialization in order to operate in the correct mode regardless of the timing of the initialization. I can see 2 ways of doing it:
@samtstern Any thoughts? |
I think this recent issue on And also this other one: |
@Feiyang1 I do think that |
I think auth looking for a global for emulator initialization would be a fine solution. If I were making it I would use an array of args, so it wouldn't be a breaking change when auth decides to change their API to allow options like Firestore and others are doing: globalThis.FIREBASE_AUTH_EMULATOR = ['localhost:9099', { /* some future args */ }];
// in auth/internal-auth initialization
if (globalThis.FIREBASE_AUTH_EMULATOR) {
this.useEmulator(...globalThis.FIREBASE_AUTH_EMULATOR);
} Or perhaps a object based on app name, incase they had multiple apps with different settings: globalThis.FIREBASE_AUTH_EMULATOR = {'[DEFAULT]': ['localhost:9099']};
// in auth/internal-auth initialization
if (globalThis.FIREBASE_AUTH_EMULATOR?.[app.name]) {
this.useEmulator(...globalThis.FIREBASE_AUTH_EMULATOR[app.name]);
} Another option would be if firestore/database Though are there any other non-emulator settings (such as tenancy) that internal-auth will have expected auth to be initialized before-hand and will respond destructively to if not? |
For those using AngularFire and are struggling with this issue: Initialize your firebase app like so:
Giving an app name (and I think you can give it '[DEFAULT]' as a name here and skip it in the imports declaration), the FirebaseAppFactory of AngularFire will return the existing instance, rather than creating a new one. |
@athoma13 do note that that your solution loses all the benefits of dynamic imports. Your main bundle will have the entirety of Firebase & reduce your application's performance—even in prod. A better solution might be:
// Work around for https://github.com/firebase/firebase-js-sdk/issues/4110
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/functions';
import { environment } from '../environments/environment';
const app = firebase.default.initializeApp(environment.firebase, 'myapp');
app.auth().useEmulator('http://localhost:9099');
app.firestore().useEmulator('localhost', 8080);
app.functions().useEmulator('localhost', 5001);
// Don't do anything, lean on AngularFire's DI init Import in your app module and do a file replacement in your production target in your |
My apologies for pinging everyone on this issue. @jamesdaniels I follow all the concepts and reasoning behind your solution except for how to import this into the app module. Since nothing is exported in this file how or what needs to be done to accomplish this? Thank you for your input! |
@jessycormier Firebase is not a pure library (in the functional programming sense). The Firebase app and it's auth, firestore, functions instances are initialized in the global scope. So if you simply import this as a "side-effect" in your app module |
@jamesdaniels Thanks for this information. I had originally tried this but had seen some interesting side effects. Auth was now using the live env while still showing the banner on the bottom for it being emulated. I assume I have some other setup conflicts that I'll have to figure out. Thanks very much for your time and knowledge! |
I tried this work around but it does not seem to be working for me. While functions and firestore are corretly using the emulator auth is still hitting the cloud. Also, I had two issues with the code throwing compiling errors:
I am using Angular 9.1.13, Firebase 8.9.1 and AngularFire 6.1.5. Any ideas what I might be doing wrong? |
Any update on this? |
@jamesdaniels - Given the release of v9, do we still have a way forward here? This looks like it need to be addressed in Auth, but I am not sure if there are any implications for Firestore and Database. |
Agreed, this is a non-issue in v9 |
If Firestore or Database (w/
useEmulator
) are initialized before Firebase Auth and an emulated user had previously signed in then a 400 error is logged to the console and the emulated auth credential are disposed of.This is unexpected as one does not need to load the auth SDK to get correct behavior with production tokens. As such I promote lazy-loading Auth only when it's needed. Also FWIW both AngularFire and ReactFire dynamically import the SDKs & it's leading to reports such as this angular/angularfire#2656
My expectation would be that if
useEmulator
is called on Firestore or Database then the SDKs would not throw away emulated auth tokens, instead use them to contact the emulator, even if auth hasn't been initialized withuseEmulator
yet itself.The text was updated successfully, but these errors were encountered: