How do I set current user using hydrated storage instead of cachedClient
Like its done in bloc docs for firebase login?
They wrote in user repository:
Stream<User> get user {
return _firebaseAuth.authStateChanges().map((firebaseUser) {
final user = firebaseUser == null ? User.empty : firebaseUser.toUser;
_cache.write(key: userCacheKey, value: user);
return user;
});
}
User get currentUser {
return _cache.read<User>(key: userCacheKey) ?? User.empty;
}
I have isEmpty/isNotEmpty in my User model but in firebase there is a getter current user where cachedClient used
In my user repository i only have
Future<User> getUser({required String username, required String password}) async
and thats it
But for changing status they added new bloc where they read this current user to set authenticated/unauthenticated
This is how its done for firebase login: https://bloclibrary.dev/#/flutterfirebaselogintutorial?id=authentication-repository
Related
there guys, I do have an interesting problem here and I would be really glad if any of you it will be able to help me with that.
What's my app flow:
Register with the email, password and some other details:
User firebase in order to auth the user and create an account via email and password, at the same time I'm writing the custom data of the user to the database.
Log in the user.
That's it, that's all my basic logic, and how you can see I'm not doing any reading from the DB so far as I know.
Now... the problem is that from some weird reason when I'm registering my user I'm going to the firebase console to see the usage of my DB and I will see something like... for one user which was created I will have 1 write (which is fine as I was expected) but also 13-20 READS FROM DB.
Now that's my question, WHY on earth I have reads on firestorm when I'm doing just auth and writes?
Here it's my DB code which I'm using right now.
class DatabaseFirebase implements BaseDataBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseStorage _storage = FirebaseStorage.instance;
FirebaseUser _firebaseUser;
Firestore _firestore = Firestore.instance;
#override
Future<String> login(String email, String password) async {
_firebaseUser = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return _firebaseUser.uid;
}
#override
Future<String> register(String email, String password) async {
_firebaseUser = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return _firebaseUser.uid;
}
#override
Future<UserData> getCurrentUser() async {
if (_firebaseUser == null)
_firebaseUser = await _firebaseAuth.currentUser();
UserData user = UserData();
user.email = _firebaseUser?.email;
user.name = _firebaseUser?.displayName;
return user;
}
#override
Future<void> logout() async {
_firebaseAuth.signOut();
}
#override
Future<void> onAuthStateChanged(void Function(FirebaseUser) callback) async {
_firebaseAuth.onAuthStateChanged.listen(callback);
}
#override
Future<void> writeUser(UserData user) async {
_firestore.collection("Users").add(user.toMap()).catchError((error) {
print(error);
});
}
}
If some of you know could you explain to me where/how I need to search in order to find this bug? Because how you can see I'm not using any read what so ever.
It's impossible to know for sure given that we don't understand all possible routes of access into your database, but you should be aware that use of the Firebase console will incur reads. If you leave the console open on a collection/document with busy write activity, the console will automatically read the changes that update the console's display. This is very often the source of unexpected reads.
Without full reproduction steps of exactly all the steps you're taking, there's no way to know for sure.
Firebase currently does not provide tools to track the origin of document reads. If you need to measure specific reads from your app, you will have to track that yourself somehow.
I want when user click to admin profile, user can see the admin info.
Im using the currentUser to get the id of current user who logged in already in applications.
I'm trying to know how get the another user data.
String currentuser = FirebaseAuth.getInstance().getCurrentUser().getUid();
// init firebase database
mUserDatabaseReference = FirebaseDatabase.getInstance().getReference("Users");
mUserDatabaseReference.child(currentuser).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// here using Picasso for get the image url and set in ImageView
String imageUrl = dataSnapshot.child("profile_pic").getValue().toString();
Picasso.with(AboutCompany_for_users.this).load(imageUrl).into(companyPhoto);
String name = dataSnapshot.child("name").getValue().toString();
String email = dataSnapshot.child("email").getValue().toString();
String phone = dataSnapshot.child("mobile").getValue().toString();
String busesNumbers = dataSnapshot.child("buses_numbers").getValue().toString();
String lineName = dataSnapshot.child("bus_line").getValue().toString();
// here we get the data from
companyName.setText(name);
companyEmail.setText(email);
companyPhone.setText(phone);
companyBusesNumbers.setText(busesNumbers);
companyLineName.setText(lineName);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
driversInformations.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent driversInfo = new Intent(AboutCompany_for_users.this, CompanyDrivers.class);
startActivity(driversInfo);
}
});
I expect when user click on admin profile show the admin info not current user info
You can't query other user accounts in Firebase Authentication from client code. You will have to either
Store the other user's data in a database, and query that database.
Call some backend code and use the Firebase Admin SDK to perform the query, and return that to the client.
Usually people choose option 1.
Create a Cloud Functions and use the Firebase Admin SDK.
You can get another user data by uid or email or phone number.
And you can list user each by max 1000.
If you create a Cloud Functions using node.js then code is like this.
// get another user data by uid
exports.getUser = functions.https.onCall(async (data, context) => {
try {
// if you want to deny unauthorized user
// - context.auth.token.xxx is customClaims. (if you use)
// if (context.auth || context.auth.uid || context.auth.token.xxx) {
// return Promise.reject("unauthorized");
// }
const user = await admin.auth().getUser(data.uid);
return {
displayName: user.displayName,
email: user.email,
photoURL: user.photoURL
};
} catch (error) {
// If user does not exist
if (error.code === "auth/user-not-found") {
return {};
}
throw error;
}
});
See:
https://firebase.google.com/docs/functions/callable
https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data
You can create a Firebase cloud functions and you can call that function from your Android app
Next, under that function, you can retrieve the data of the corresponding user and return it as a result to your Android app.
I use this code to define Current user and email in Firebase:
var CurrentUserEmail = FirebaseAuth.Instance.CurrentUser.Email;
It doesn't give the right user and email. Did you have the same issue?
I am not familiar with xamarin, but if you want to get a the current user or his/her email then you should make sure that the user is signed in or else you can't get these details, something like that:
//get the current user like that
var currentuser =FirebaseAuth.Instance.CurrentUser;
//check is user not null (in other words if user signed in)
if(currentuser != null){
//this means user is signed in
//get user uid
var uid= currentuser.Uid;
//get user email
var email= currnetuser.Email;
}
I found my mistake, in case who will meet the same case. I typed StartActivity right away after SignInWithEmailAndPassword:
auth.SignInWithEmailAndPassword(email,password)
.AddOnCompleteListener(this,this);
StartActivity(intent); // incorrect
Right way to do it :
auth.SignInWithEmailAndPassword(email,password)
.AddOnCompleteListener(this,this);
...
public void OnComplete(Task task)
{
if(task.IsSuccessful){
StartActivity(intent);
Finish();
}
}
first you must declare private FirebaseAuth _auth; as a property of your activity.
Then you can access your currentUser this way :
FirebaseUser currentUser = _auth.CurrentUser;
I send email verification and click the link. Then I call isEmailVerified() on FirebaseUser object. This always return null, what may be the reason?
You should refresh the user object after verification as follows :
usertask = mAuth.getCurrentUser().reload();
usertask.addOnSuccessListener(new OnSuccessListener() {
#override
public void onSuccess(Void aVoid) {
user = mAuth.getCurrentUser();
boolean useremailveri = user.isEmailVerified();
String useremailuid = user.getUid();
}
});
First you have to call this method : signInWithEmailAndPassword , at least 1 time after user has verified their email ....
Only after that, you will get isEmailVerified() as true
Change isEmailVerified() to emailVerified;
Re-authenticates the user who updates if the email has been verified ...
https://firebase.google.com/docs/auth/android/manage-users#re-authenticate_a_user
Same technique as #yehyatt´s solution but easier. Sign the user in with firebase, refresh the user and then check if the user is verified.
Sign the user In
Auth.auth().signIn(withEmail: YourEMail, password: YourPassword) { result, error in
if error != nil {
logInErrorReason = "\(error!.localizedDescription)"
// An error ocurred
}else {
// Handle successful login.
}
}
Refresh current user:
Auth.auth().currentUser?.reload(completion: ((Error?) -> Void)?)
Check if he is signed in:
if Auth.auth().currentUser?.isEmailVerified == true { // Handle successful verification}
I think you need to first login with user's Email and Password using signInWithEmailAndPassword then try calling isEmailVerified()
method on returning user.
isEmailVerified should provide correct response, however you might be using it incorrectly.
If user verified his email ( he/she received an email from firebase, and clicked on "verify email" link ) then it will return true;
If user didn't verified his email - it will return false
So solution for this issue is to update you registration process.
When you create new user you should also issue a command to start email verification process await user.sendEmailVerification();
once your user will receive an email and will verify it. your method : user.isEmailVerified will return true
Your registration process should look something like that:
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email,password: password);
FirebaseUser user = result.user;
await user.sendEmailVerification();
Create new user using createUserWithEmailAndPassword
Let it send verification link to your gmail using sendEmailVerification
go to your own gmail, you will see verification link and just click
on that link
try to login with username and email
You will see your own account had already verified and can proceed next setps
https://github.com/FirebaseExtended/flutterfire/issues/2208
When I signIn with my google account and get the name with the getDisplayName(), my name appear correctly, but in the AuthStateListener doesn't.
here part of my code:
private void handleSignInResult(GoogleSignInResult result) {
Alert.dismissProgress();
if (result.isSuccess()) {
GoogleSignInAccount acct = result.getSignInAccount();
if(acct != null) {
Log.i("NAME", acct.getDisplayName()); <-- RETURN MY NAME CORRECTLY
credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
fuser.linkWithCredential(credential).addOnCompleteListener(authResult);
} else {
//ERROR
}
} else {
//ERROR
}
}
But in my AuthStateListener
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser nuser = firebaseAuth.getCurrentUser();
if (nuser != null) {
Log.i("NAME", nuser.getDisplayName()); <--- RETURN NULL
}
}
Somebody know why this can happen?
This is a tricky one since it is not so clear in the documentation...
Check the getProviderData()
as defined here:
https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseUser#public-method-summary
You can iterate that List and it will have all the providers associated with that account, included a provider with the providerId = "google.com" with a display Name = YOUR_GOOGLE_USERNAME
let me know if you cannot make it work
Just to add to Ymmanuel's answer (Thank you!) with some example code for anyone else looking for a quick copy and paste:
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
String displayName = user.getDisplayName();
Uri profileUri = user.getPhotoUrl();
// If the above were null, iterate the provider data
// and set with the first non null data
for (UserInfo userInfo : user.getProviderData()) {
if (displayName == null && userInfo.getDisplayName() != null) {
displayName = userInfo.getDisplayName();
}
if (profileUri == null && userInfo.getPhotoUrl() != null) {
profileUri = userInfo.getPhotoUrl();
}
}
accountNameTextView.setText(displayName);
emailTextView.setText(user.getEmail());
if (profileUri != null) {
Glide.with(this)
.load(profileUri)
.fitCenter()
.into(userProfilePicture);
}
}
The above will try to use the first display name and photo url from the providers if it wasn't initially found in the User object.
Bonus: Using glide for images: https://github.com/bumptech/glide .
Edmund Johnson is right. This issue was introduced in Firebase Auth 9.8.0. A workaround includes downgrading to 9.6.1 or forcing 're-login' as the info is populated after the user logs out and logs back in. The problem is described in the Firebase issue
It has been reported as a bug to Firebase by one of the Firebase UI contributors - Alex Saveau.
I had the same issue, I solved it by signing out the user and resigning them back in.
Call FirebaseAuth.getInstance().signOut(); to sign them out, then try again.
As I've discovered, this issue is common with using email/password authentication and Social login (Facebook in my case) at the same time.
I found a solution for this problem, in the Firebase documentation!
The solution is to update the user profile using the: UserProfileChangeRequest:
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(mUser.getName())
.setPhotoUri(Uri.parse("https://example.com/mario-h-user/profile.jpg"))
.build();
firebaseUser.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User profile updated.");
}
}
});
The variable mUser is already filled with the content from the fields.
I used this piece of code inside the FirebaseAuth.AuthStateListener() -> onAuthStateChanged()
This issue is resolved in the latest verison of firebase ui
compile 'com.firebaseui:firebase-ui:1.2.0'
First let me say there's no need to downgrade the Gradle files or logout and login the user multiple times as stated by others just to display the user name. I've solved the issue a different way while still keeping the latest Gradle files and not having to log the user out and in multiple times. The issue with getDisplayName() is a very big one so I want to be as descriptive as possible for future users of Firebase to spare them the headache.
Here are the details of the solution:
Solution 1:
For users who authenticate(sign-in) with multiple providers such as Facebook, Google etc. and/or Email/Password that they created at sign-up:
The first thing you want to ensure is that when you have a user sign-up with the app for the first time you store their name of course to the database under their unique id. Which may look something like this:
// Method signature. Write current user's data to database
private void writeNewUser(String userId, String name, String email) {
DatabaseReference current_user_database = mDatabaseRef.child(userId);
current_user_database.child("username").setValue(name);
// Email here is not mandatory for the solution. Just here for clarity of the
// method signature
current_user_database.child("email").setValue(email);
}
After the user's name has been stored in your Firebase database and you have them sign into your app you can get their username something like this:
// Method that displays the user's username
private void showUserName() {
// Get instance of the current user signed-in
mFirebaseUser = mAuth.getCurrentUser();
// Check if user using sign-in provider account is currently signed in
if (mFirebaseUser != null) {
// Get the profile information retrieved from the sign-in provider(s) linked to a user
for (UserInfo profile : mFirebaseUser.getProviderData()) {
// Name of the provider service the user signed in with (Facebook, Google, Twitter, Firebase, etc.)
String name = profile.getDisplayName();
// If displayName() is null this means that the user signed in using the email and password they created. This is the null issue we shouldn't get that we are gonna be solving below.
if(name == null){
mDatabaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get the name the user signed-up with using dataSnapshot
String nameOfCurrentUser = (String) dataSnapshot.child("name").getValue();
// Set username we retrieved using dataSnapshot to the view
mTestUsernameTextView.setTitle(nameOfCurrentUser);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
// If the name is not null that means the user signed in with a social Auth provider such as Facebook, Twitter, Google etc.
if(name != null) {
// Set the TextView (or whatever view you use) to the user's name.
mTestUsernameTextView.setText(name);
}
} // End for
} // End if
}
That's it. Now you just call that method showUserName() or whatever your method is gonna be called inside your onCreate and hopefully this helps.
Solution 2:
For users who sign into your app using ONLY a social media service provider such as Facebook, Twitter, Google or whatever other option Firebase allows:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
for (UserInfo profile : user.getProviderData()) {
// Id of the provider (ex: google.com)
String providerId = profile.getProviderId();
// UID specific to the provider
String uid = profile.getUid();
// Name, email address, and profile photo Url
String name = profile.getDisplayName();
String email = profile.getEmail();
Uri photoUrl = profile.getPhotoUrl();
};
}
That's it for solution 2, just follow the guidelines on Firebase for that and you should be good.
Here's a link to it if you're curious:
Get a user's profile information
I truly hope this helps anyone struggling with the getDisplayName() issue.
Conclusion:
If your app only has Facebook, Twitter, Google or whatever else social media sign-in options Firebase provides then just simply calling getDisplayName()method on the currently signed in user should be enough to show their username.
Otherwise if your app allows the user to sign in using an email/password they created then make sure you got their name/username at sign-up so that you can use it later on to display it.
I was getting this problem when I had:
implementation 'com.google.firebase:firebase-auth:10.0.0'
implementation 'com.firebaseui:firebase-ui-auth:1.0.1'
A workaround that fixed it for me was to replace that with:
implementation 'com.google.firebase:firebase-auth:9.6.0'
implementation 'com.firebaseui:firebase-ui-auth:0.6.0'
Iterating through user.getProviderData() as suggested elsewhere didn't fix it for the later versions.
Based on Alan's and Ymmanuel's answers here's the 2 helper methods that I'm using:
public static String getDisplayName(FirebaseUser user) {
String displayName = user.getDisplayName();
if (!TextUtils.isEmpty(displayName)) {
return displayName;
}
for (UserInfo userInfo : user.getProviderData()) {
if (!TextUtils.isEmpty(userInfo.getDisplayName())) {
return userInfo.getDisplayName();
}
}
return null;
}
public static Uri getPhotoUrl(FirebaseUser user) {
Uri photoUrl = user.getPhotoUrl();
if (photoUrl != null) {
return photoUrl;
}
for (UserInfo userInfo : user.getProviderData()) {
if (userInfo.getPhotoUrl() != null) {
return userInfo.getPhotoUrl();
}
}
return null;
}