I am using Firebase on both Android and iOS and after I register a user, I pass the username I took from the user, to my server to update the displayname. This works, but it's not visible on the app until the user closes the app or logs out.
I have tried using the reload method which states
Manually refreshes the data of the current user (for example, attached providers, display name, and so on).
But this doesn't do anything.
Is there another way of refreshing/reloading the data, or maybe clearing the cache?
It's possible that I could add the displayName in the app itself, but as I limit the displayname based on availability of names in my server, it makes sense to do it there.
UPDATE: (have added some code examples)
I update the displayname in a java app using the admin sdk like this
private void updateUsernameInFirebase(String uniqueId, String username){
UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uniqueId).setDisplayName(username);
try {
UserRecord userRecord = FirebaseAuth.getInstance().updateUser(request);
} catch (FirebaseAuthException e) {
e.printStackTrace();
}
}
and then try to refresh the data in my Android client app like this
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if(firebaseUser != null) {
Task<Void> reload = firebaseUser.reload();
}
Related
I need to detect and differentiate two users using Firebase phone authentication. This should be done before granting a privilege to enter into the home activity of the app. When I did as suggested here (Stackoverflow), it does well by detecting the user using timeStamp() method. The answer does its job but the fancy thing is I need some data input from the new user before the verification code is sent.
In order for a verification code to be sent, a user provides a number which is directly authenticated in the firebase. Hence I cannot check if it's a new user (phone number) or current user (phone number).
Here is the code using TimeStamp() method.
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential)
{
_firebaseAuth.signInWithCredential(credential).addOnCompleteListener(Objects.requireNonNull(getActivity()), task ->
{
if(task.isSuccessful())
{
//Sign in success, update UI with the signed-in user's information.
FirebaseUser _user = Objects.requireNonNull(task.getResult()).getUser();
long creationTimestamp = Objects.requireNonNull(Objects.requireNonNull(_user).getMetadata()).getCreationTimestamp();
long lastLoginTimestamp = Objects.requireNonNull(Objects.requireNonNull(_user).getMetadata()).getLastSignInTimestamp();
if(creationTimestamp == lastLoginTimestamp)
{
//Create a new user with account
setUserDataToDatabase(_user, _username, _university, _course, _year);
sendUserToWelcome();
}
else
{
//User exists, just login
sendUserToHome();
}
}
else
{
FancyToast.makeText(getContext(), "Enter sent code", FancyToast.LENGTH_SHORT, FancyToast.INFO, false).show();
}
});
}
After several research with no success. I decided to walk around, I'm using firestore database. I decided to track every user's number in a new collection with auto-generated document id. I called the collection USERS whereas each document has a unique random id.
I get the user's number and check it if any of the registered user has that number with the USERS's collection using a whereEqualTo() method with the phone_number field. If the number is exists I login the user else display a registration screen.
_firestore.collection(USERS).whereEqualTo("phone_number", _phoneCheck).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>()
{
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task)
{
if(task.isSuccessful())
{
//If task is greater than 0 means there is a presence of a phone number.
if(Objects.requireNonNull(task.getResult()).size() > 0)
{
//Here I allow user to login as usual.
PhoneAuthOptions options = PhoneAuthOptions.newBuilder(_firebaseAuth).setPhoneNumber(_phone).setTimeout(60L, TimeUnit.SECONDS).setActivity(Objects.requireNonNull(getActivity())).setCallbacks(_callbacks).build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
}
else
{
//Else the task is empty means there is no a presence of a phone number.
//Check if there is a presence of registration data to bind with new user.
if(_registrationData != null)
{
//I login user with the new data and save the information into the firestore plus the phone number.
PhoneAuthOptions options = PhoneAuthOptions.newBuilder(_firebaseAuth).setPhoneNumber(_phone).setTimeout(60L, TimeUnit.SECONDS).setActivity(Objects.requireNonNull(getActivity())).setCallbacks(_callbacks).build();
PhoneAuthProvider.verifyPhoneNumber(options);
userInputs();
}
else
{
//Display a welcome a screen to register an account.
FancyToast.makeText(getContext(), "Welcome! Open an account", FancyToast.LENGTH_SHORT, FancyToast.INFO, false).show();
}
}
}
}
});
Allowing unauthenticated user to have a privilege into the database is very risk. Hence, I implemented a rule to allow unauthenticated user to read only.
match /USERS/{document=**}
{
allow read: if true;
}
Though this still is risky, any rule suggestions I will be grad and appreciable.
I am storing user FCM device tokens in Firebase. When the user logs in, the token is added to the user's profile like this:
if (FirebaseAuth.getInstance().getCurrentUser()!=null) {
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
DeviceToken token = new DeviceToken(instanceIdResult.getToken());
CollectionReference deviceTokens = mUserCollection.document(mSignedInUserID).collection("device_tokens");
deviceTokens.document(token.getTokenID()).set(token);
}
});
}
This works. However, I also want to delete that document when the user signs out. I am attempting to do so like this:
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
Log.d(TAG,instanceIdResult.getToken());
DocumentReference deactivatedToken = mUserCollection.document(mSignedInUserID).collection("device_tokens").document(instanceIdResult.getToken());
deactivatedToken.delete();
mAuth.signOut();
recreate();
}
});
Everything works in that method except for the actual deletion of that document, and the log statement confirms that the user's current ID matches the title of the document to be deleted. A simulation for a signed in user writing to that location returns allowed. What am I doing wrong?
Note that, with your code, the user is going to get signed out before the document is deleted. That's because the delete() is asynchronous (as well as all database operations), and returns immediately, before the work is complete. If I had to guess, I'd say that your authentication token is getting wiped out before the delete operation actually gets sent, and the delete is effectively acting as an unauthenticated user. So, what you should do is wait for the delete to complete for actually signing out the user. Use the Task returned by delete() to know when that finishes. It'll work the same way as the Task returned by getInstanceId().
How to identify the id (from push) of existing user?
I created a node using push when a user is registered, but when the user try to login again, I don't know how to identify it directly because its a different key.
Is it wrong to use push instead of the authentication id as my node? Because I want to retrieve the data I kept in my db for that user.
// [START auth_state_listener]
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) { //I WANT TO RETRIEVE DB DATA HERE
You ask: Is it wrong to use push instead of the authentication id as my node? The answer is almost certainly, yes. The documentation for FirebaseUser.getUid() states:
Returns a string used to uniquely identify your user in your Firebase
project's user database. Use it when storing information in Firebase
Database or Storage, or even in your own backend
The completion of the code in your post would be something like:
if (user != null) {
DatabaseReference ref = FirebaseDatabase.getInstance()
.getReference("users").child(user.getUid());
ref.addListenerForSingleValueEvent(...);
}
In one activity, I am signing in the user, and it moves on to another activity where the user is greeted. I am using the built-in Firebase methods for signing in a user using email/password. I have the UID in Firebase Database and this is linked with a name. I am just manually inputting users in Firebase and will implement a sign up activity later. How do I access the UID of the user in the second activity?
Alternatively to passing the uid around, you can use an auth state listener to detect the user in each activity. From that documentation page:
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d(TAG, "onAuthStateChanged:signed_out");
}
// ...
}
};
This code would typically go into a base-class that you derive your activities from.
just use
FirebaseAuth mAuth;
mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
String UID = user.getUid();
It will automatically get user from previous acivity.
You should either save it in SharedPreferences and retrieve it in the second activity or pass it into the second Activity via the Intent object.
SharedPreferences is one of the simpler ways to store data persistently in the app. Data stored in SharedPreferences can be retrieved and modified from almost anywhere in the app. Data will survive phone restarts, and even an app uninstall, because it can be backed up by the system. Here is the official explanation. And here is a good tutorial.
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;
}