In my Angular/Firebase app, I have a user service which supports sign-in and sign-out. After sign-in, I retrieve app-specific data for the user from my Angular database. The service publishes an observable called appUser$ which contains the current user object (or null, if no-one is signed in). Things look like this. I'm using switchMap to switch between different observables for my app's user data as users sign in and sign out.
constructor(
private angularFireAuth: AngularFireAuth,
private angularFireDatabase: AngularFireDatabase
) {
this.appUser$ = this.angularFireAuth.authState
.map(user => user && user.uid)
.switchMap(uid => uid ? this.angularFireDatabase.object(`/users/${uid}`) :
Observable.of(null);
}
signOut() { return this.angularFireAuth.auth.signOut(); }
The problem with this is that the instant sign-out occurs, the permissions for accessing the /users node in the database are lost, generating an permission denied run-time error. To deal with this, I tried creating my own Subject for Angular User and zero it out before calling signOut. I thought that this would trigger the switchMap and somehow cancel the pending object observable for the previous user's app data, thereby preventing the permission denied error.
user$: Subject;
constructor(
private angularFireAuth: AngularFireAuth,
private angularFireDatabase: AngularFireDatabase
) {
this.angularFireAuth.authState.subscribe(this.user$);
this.appUuser$ = this.user$
.map(user => user || user.uid)
.switchMap(uid => uid ? this.angularFireDatabase.object(`/users/${uid}`) :
Observable.of(null);
}
signOut() {
this.user$.next(null);
return this.angularFireAuth.auth.signOut();
}
But the same permission problem persists, even if I delay the call to signOut. It seems as if somehow the observable created by this.angularFireDatabase.object is somehow still "alive" even after the switchMap should have switched to the Observable.of(null), and Firebase is still unhappy about losing permissions to access it.
I could trap or suppress this error somehow, but is there some simple way to deal with this situation?
Related
I have made a sign-in page, and a sign-up page with Firebase Authentication in Flutter and Dart.
After the sign up, I'm trying to retrieve the current user's displayName, however, when I try retrieving it, I seem to get not the current one, but the one that I signed up with before this one.
However, when I for example hot-restart the app, I get the current user's details just fine.
I try to retrieve the current user's displayName property with this code:
static String? getUsername() {
return FirebaseAuth.instance.currentUser?.displayName!;
}
The way I call this, is I initialize a variable to store the username which I get from the method, on a different dart file, different from the signUp page I got. I also call this method in the initState() method.
This is how I sign-up the user and set the displayName:
static void signUpUser(String username, String emailAddress, String password) async {
try {
final credential =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: emailAddress,
password: password,
);
// Here I set the displayName property
await credential.user!.updateDisplayName(username);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {}
else if (e.code == 'email-already-in-use') {}
} catch (e) {}
}
I tried to use the user.reload(), and FirebaseAuth.userChanges() functions, but these did not seem to fix my problem.
Maybe I'm trying to retrieve the displayName property wrong, what am I missing? I'm quite new to developing my own apps and working with Firebase.
The Future that updateDisplayName returns completes when the call has been made to the underlying Firebase SDK. It does not automatically update the user profile in your application at that point though. That will only happen automatically once every hour (when the SDK refreshes the ID token on which that profile is based), or when the user signs out and in again.
To force a refresh of the profile from your application code outside of those automatic conditions, you can call reload() on the user object.
Using Flutter 1.20.2.
My Flutter app uses Firestore as it's backend database. For the current version I am using and throughout the development of this mobile app I have noticed that if my app is in the background for a period of time (could be a few mins) then when I bring the app back into the foreground the queries are very slow to return data. This does not happen on iOS. It only happens on Android.
I use CircularProgressIndicators when my app is busy retrieving data from Firestore. I am using a solid state management setup where each of my Views have a model that extends a BaseModel:
class BaseModel extends ChangeNotifier {
ViewState _state = ViewState.Idle;
ViewState get state => _state;
bool isDisposed = false;
void setState(ViewState viewState) {
_state = viewState;
if (!isDisposed) {
notifyListeners();
}
}
#override
void dispose() {
isDisposed = true;
super.dispose();
}
}
My views then use my view specific models in the following way:
#override
Widget build(BuildContext context) {
return BaseView<MyProfileModel>(
//onModelReady: (model) => model.initialise(Provider.of<User>(context, listen: false)),
onModelReady: (model) => model.initialise(),
builder: (context, model, child) => Scaffold(
resizeToAvoidBottomInset: false,
...
I do not use the AppLifecycleState class yet to do anything special when the app is in the background or is resumed from the background.
When my model is busy retrieving data I show busy circular progress indicators.
The issue is that when I resume my app from the background into the foreground, sometimes the app could be busy for up to 1 minute before it retrieves the data - but only the first time after being back in the foreground. All subsequent calls are normal. Sometimes, it even hangs on first attempt to get data after coming back to the foreground.
I feel like I am not implementing a best practice in relation to resuming an app into the foreground that uses the Firestore database. I have a suspicion that it has something to do with re-establishing the Firestore connection and/or local cache. My App uses the default settings for these.
All of my Firestore API calls are contained in it's own class and I call it the same way each time:
await Firestore.instance
.collection(DBStrings.COLLECTION_AD_MESSAGES)
.document(ad.adId)
.collection(DBStrings.COLLECTION_CHILD_AD_MESSAGES)
.document()
.setData({
// Set fields...
}).catchError((e) {
res = false;
});
Can someone give me some insight into this issue and what could be potentially causing it?
It seem to me that your app is loosing the connection and the data retrieved is from the cache. My suggestion is for you to try to change the backend data from the Firebase console while your app is in the background, then test to see if the retrieved data is the updated or the old one.
If the data is the old one, it means your app could not restore the connection. To overcome this problem you need to check the auth status (if used) and to check the connection status. A simple way to identify connection status and not allow the app to take a very long time before going cache, is to force the app to ask data from remote and provide a timeout, like this:
QuerySnapshot snapshot = await query.getDocuments(source: Source.server).timeout(
_timeoutDuration,
// this or any other callback to handle timeout
onTimeout: () => query.getDocuments(source: Source.cache));
If you are using auth, you can check the auth status by calling:
FirebaseUser currentUser = await _auth.currentUser();
if (currentUser != null) {
// Handle your auth problem here
}
If you are not using auth and the app is retrieving the data from the server after this long period, check if the app would come back faster without the firebase query.
I have an ionic3 application using angularfire2 and firebase. I use firbase auth to login to my application, and for retrieving an object from firebase about "currentChallenges". When I use the logout function an error is thrown from Firebase.
Error message:
permission_denied at /currentChallenge: Client doesn't have permission
to access the desired data.
I use the following function for my logout, in my auth.service.ts:
logout() {
this.isLoggedIn = false;
this.firebaseAuth
.auth
.signOut().then(() => {
this.appCtrl.getRootNav().popToRoot(); //root is login page
});
}
I am not sure where/what exactly is causing the error. In my challenges.service.ts is where I make the initial observable object:
private dbCurrentChallengesObservable: FirebaseObjectObservable <any>;
constructor(db: AngularFireDatabase) {
this.dbCurrentChallengesObservable = db.object('/currentChallenge');
}
public getCurrentChallenges(){
return this.dbCurrentChallengesObservable;
}
And then, I use this object in my model (challenges.ts) like this:
ionViewCanEnter(): boolean{
return this.authService.isAuthenticated();
}
ionViewDidLoad() {
this.currentChallenge = this.challengesService.getCurrentChallenges();
this.currentChallenge.subscribe(data => {
if(data.challenge1completed > 0) this.challenge1Completed = true;
if(data.challenge2completed > 0) this.challenge2Completed = true;
});
}
At first I thought it was related to the subscribe and I added an subscribe().unsubscribe() in an on-ion-leave function in the challenges.ts, but that did not stop the error. But something must still be listening to firebase, which must be stopped upon or even before logout. I just don't know what/where/how.
Any help would be appreciated.
private afDatabase: AngularFireDatabase,//blabla
this.afDatabase.database.goOffline();//To solve the permission denied 문제.
add above statement before signout();
I too took a lot of time on this problem and not a single clue found.
Hope this helps
I am using Firebase authentication in my app and signing up a user with email and password. I want to get other users details (separate from the logged-in user) as well while a user is signed in with their own account. How can I get that information?
Values like email, display name and id (specific to authentication system) are available off of the Firebase User object. You can get a reference to the current logged in user off of the FIRAuth class. I provided links and class names for iOS, but other platforms are similarly structured.
If you want to store additional data for users, I would recommend including a users root node, using the uid off of the Firebase User object as the key for users child nodes.
//create user
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(SignupActivity.this, new OnCompleteListener < AuthResult > () {
#Override
public void onComplete(#NonNull Task < AuthResult > task) {
Toast.makeText(SignupActivity.this, "createUserWithEmail:onComplete:" + task.isSuccessful(), Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Toast.makeText(SignupActivity.this, "Authentication failed." + task.getException(),
Toast.LENGTH_SHORT).show();
} else {
String user_id = auth.getCurrentUser().getUid();
DatabaseReference current_user_db = _Database.child(user_id);
current_user_db.child("name").setValue(name);
current_user_db.child("image").setValue("default");
startActivity(new Intent(SignupActivity.this, ProfileActivity.class));
finish();
}
}
});
}
});
It's not a security issue, but just a mean of how you treat personal information. You can store what you want in firebase so you can easily store when the user login his/her avatar url (aka facebook url) or just id or any other infos anyway, then retrieve it.
If you need to retrieve infos of users which are not using your apps beside, thden you can also easily via the facebook sdk with user permission of course. take care–
I am writing an Android application and I am trying to log users in anonymously so they don't have to go through any sort of registration process. I am storing their anonymous user ID in shared preferences, and when the application opens, I am trying to log them in based on that user ID. I am trying to figure out the correct way to do this, as there doesn't seem to be an auth function that just takes in a UID. Currently I have it using auth(), but I don't feel like that is correct.
Here is some sample code:
String userID = getUserID();
if(userID.equals("NOT_FOUND")) {
ref.authAnonymously(new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
//successful authentication
//save auth data
SharedPreferences prefs = getSharedPreferences(
"USER_ID", Context.MODE_PRIVATE);
String id = authData.getUid();
prefs.edit().putString("USER_ID", id).commit();
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
//unsuccessful authentication
}
});
} else {
ref.auth(userID, new Firebase.AuthListener() {
...
You're creating a new authentication session each and every time you invoke FirebaseRef.authAnonymously(...). This method only needs to be invoked once, after which the user will authenticated upon page refreshes. Also note that you do not need to call FirebaseRef.auth() again once restarting the application, as that piece is automatically handled for you.
If you'd like to check for the current authentication state of the user, and only then create a new authentication session if the user is not currently authenticated, use the synchronous accessor for authentication state FirebaseRef.getAuth().
Lastly, once you create an anonymous authentication session, no new sessions may ever be created with the same uid. That session will live until your predefined session expiration time (configured in your account dashboard) or until your user logs out, after which that uid is permanently retired.