Firestore rules not working using displayName, Android - android

I have firestore rules like this:
match /{document=**} {
allow read, write: if request.auth.token.name == "dummyUser";
}
Now when I createUserWithEmailAndPassword, after I created the user, I set the displayName to "dummyUser" like this, with method setUsersSecureName() that I made:
fun setUsersSecureName(myCallback: (Boolean?) -> Unit) {
val user = FirebaseAuth.getInstance().currentUser
val profileUpdates = UserProfileChangeRequest.Builder()
.setDisplayName("dummyUser")
.build()
user?.updateProfile(profileUpdates)
?.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG_HELPER_METHODS, "Secure user profile updated.")
myCallback(true)
}
}
}
I do the same for anonymous logins also. So I create anonymous log in like this:
auth.signInAnonymously()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG_MAIN, "signInAnonymously:success")
val user = auth.currentUser
setUsersSecureName(){
makeRequest()
}
As you see, after login is successful with anonymous user, I change the users name. Now it seems to be changed, when I check it, the users displayName is "dummyUser". However, the firestore request doesn't work, and I get the message in the log:
PERMISSION_DENIED: Missing or insufficient permissions.
This is also not working when users are signed in with email and password. Why would this be?
Thanks

Security rules work by receiving a user ID token from Firebase Authentication at the time of the request. The Firestore SDK does this automatically. You normally don't have to do anything special.
However, in this specific case, after you update the user's profile, Firebase Authentication is still holding on to a user ID token that doesn't know about the change of name. You will probably also have to tell Firebase Authentication to fetch a new one by calling user.getIdToken(true) after the profile is successfully updated, in order to force a refresh of the ID token. After the refresh succeeds, then you can try the Firestore query to see if it works. user.reload() might work as well. Both of those methods are asynchronous and return a Task that you should use to track the completion of the request.

Related

Firebase Authentication returns NULL for getCurrentUser() even though there's a user in Firebase Console

When a user in a registration fragment enters a username, email, and password he is forwarded to a verification fragment where I sent him email verification and he stays on that screen until he clicks the link from the email, after that he's forwarded to profile fragment.
While the user is on the verification fragment, he can go back to the registration fragment before clicking the link from the email I sent. In that case, I call a function from onStart() to delete his data from the Firebase:
private fun deleteUnverifiedAccount() {
val firebaseAuthentication = FirebaseAuth.getInstance()
if (firebaseAuthentication.currentUser == null) {
shortToast("empty")
}
if (firebaseAuthentication.currentUser != null) {
if (!firebaseAuthentication.currentUser.isEmailVerified) {
val user = firebaseAuthentication.currentUser
user.delete()
.addOnCompleteListener {
shortToast("deleted")
}
}
}
}
Here's the problem:
This function successfully deletes the user from Firebase Authentication
But when the user tries to register the second time, his data is again being saved to Firebase Authentication.
And if at the moment he goes back again to the registration fragment without verifying his email then his data is not deleted from Firebase Authentication.
And when the deleteUnverifiedAccount() function is called it says that currentUser == null even though he is clearly not null in Firebase Authentication.
Firstly i did not try to delete any user from firebase but i have just searched something and i find this link https://www.javatpoint.com/firebase-realtime-database-update-and-delete this link will help you because i think problem of your code you try to delete your user but not user's data.
your syntax will be like this
myFirebase.child(current user's id).remove value
can you try this before user.delete() i hope it works

Firebase.auth.currentUser is never null

I need to receive the "null" value from the Firebase.auth.currentUser function, but it returns some default user.
val auth = Firebase.auth
val currentUser = auth.currentUser
if (currentUser != null) {
val action = SignInFragmentDirections.actionSignInFragmentToTransactionsFragment(currentUser)
findNavController().navigate(action)
}
Since the auth.currentUser never returns null, my application always goes to another screen and does not allow registering a new user.
Why is this happening and how can I get null when calling auth.сurrentUser?
"Does not allow registering a new user"
It seems to me you are trying to create multiple users from using the Android SDK but it is not built for that. Once you sign in, then you would have to sign out to create another user because only one user can be logged in a single application (at least of a single Firebase project).
If you want to create multiple accounts then you can do so directly from the Firebase console. However if your app is meant to be used by admin or privileged users who can create new accounts and you don't want to give them access to the Firebase console then you would have to use something like Cloud functions or your own server.
There you can use the Firebase Admin SDK to create new users. A simple functions may look like:
exports.createNewUser = functions.https.onCall((data, context) => {
return admin
.auth()
.createUser({
email: data.email
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
return userRecord.uid
})
.catch((error) => {
console.log('Error creating new user:', error);
});
});
You can call the function from your app like this:
private fun createUser(name: String, email: String): Task<String> {
// Create the arguments to the callable function.
val data = hashMapOf(
"name" to name,
"email" to email
)
return functions
.getHttpsCallable("createNewUser")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
}
}
You can read more about callable functions here.
Do note that I simply ran the create user method in the cloud functions but ideally you should check which user is calling the function (context.auth has the info) and make sure only the authorized user can use that function.
I figured it out - if I logged in app and didn't call the auth.signOut method - I deleted the account from the Firebase Console only, for this reason the auth token saved locally for a some time and for this reason auth.currentUser returned non-null. When I deleted the app (and added the signOut logic in my app later) and reinstall it, all works correct.

How to update Firebase user profile on sign in Android?

Currently, I update FirebaseUser profile after sign in, can I do it as part of sign-in parameters? i.e as an atomic operation?
The reason is that I want to avoid handling error of profile not updated correctly e.g. not a stable internet connection or app crash
I tried to update the profile name before the actual sign-in, hoping it will take it from the previous but it did not work
Please note the user before sign-in is anonymous and its uid might be different from the final signed-in user
This is my current code
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful) {
updateProfile()
} else {
codeError()
}
}
}
private fun updateProfile() {
val profile = UserProfileChangeRequest.Builder()
.setDisplayName("user name")
.build()
FirebaseAuth.getInstance().currentUser!!.updateProfile(profile)
goBackToMain()
}
Currently I update Firebase user profile after sign in
That's the right thing to do. When you call signInWithPhoneAuthCredential(), a FirebaseUser object is created. Once this object is created, it can be updated.
I tried to update the profile name before the actual sign-in, hoping it will take it from the previous but it did not work
Indeed it's the expected behavior because, before the sign-in, the FirebaseUser object:
FirebaseAuth.getInstance().currentUser
Will return null, meaning that the user is not signed in. There is no way you can update an object that that is not initialized and holds the value on null.
Please note the user before sign-in is anonymous and its uid might be different from the final signed-in user.
It won't unless the user signs-out or uninstalls the app. When you link an anonymous account with a Provider, for example with Google, the UID of the user remains the same, meaning that:
FirebaseAuth.getInstance().currentUser.uid
Is the same as before you linked the account.

UNAUTHENTICATED ERROR : Firebase function called from android app

I have just started working with callable functions on firebase.
I deployed a function that gives the response hello world.
It worked fine when I tested it locally.
Now I am trying to call it from android app.
According to this document, these callable functions automatically pass the authentication data and deserialise the data to get the user id in the function. When I run as suggested in the document above , I get an Unauthenticated error.
I have used google sign in using firebase auth in the app. I dont want to make it public and keep it private so only the users can send requests. Is there any way I could manually include authentication in the header of the request.
Here is the code from the call
sendmessage(string)
.addOnCompleteListener {
if (!it.isSuccessful){
val e= it.exception
if(e is FirebaseFunctionsException){
val code =e.code
val details = e.details
Log.d("Firebase error",code.toString()+details.toString())
}
}
else{
Log.d("result obtained",it.result.toString())
}
}
private fun sendmessage(question:String): Task<String> {
val data = hashMapOf(
"question" to question,
"push" to true
)
return functions
.getHttpsCallable("detectIntent")
.call(data)
.continueWith {
val result=it.result?.data as String
result
}
}
Callable functions don't provide any amount of control over what happens in the HTTP headers. Authentication is handled automatically, based on the state of the user signed in through Firebase Auth. If you're not getting an authenticated user in your function in context.auth, that means you made the request before sign-in was complete.

Detect Firebase Auth Provider for Loged in User

How can i check that my user Loged in to my app Using which Auth Provider ? I wanted to detect that did my user loged in to my app using Facebook auth provider or using Email Provider or by using Google auth provider . I have searched this in Firebase Docs but i couldnt find any proper answer ,
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser.getProviderData().size() > 0) {
//Prints Out google.com for Google Sign In, prints facebook.com for Facebook
e("TOM", "Provider: " + firebaseUser.getProviderData().get(firebaseUser.getProviderData().size() - 1).getProviderId());
}
You can always check the list of providers as Malik pointed out. However, as you can have multiple providers linked to the same user, to get the sign in method of the current User with multiple providers, you have to check the ID token. You need to check firebase.sign_in_provider claim in the token. That will give you the sign in method used to get the ID token. To get it on the client, you need to getIdToken and then parse the returned JWT with some JWT parser.
You can use method getIdTokenResult() of your user object (firebase.User) to get IdTokenResult object, whose signInProvider property you can use to detect signin method of your logged in user.
Reached here for same issue, unable to find the provider that user used to login.
I am working on react-js app using react-firebaseui lib for readymade UI.
After a lil struggle, I simply analysed the "user" object returned by auth and in that we receive an array "providerData".
Without further analysis I decided to use:
const signinProvider = user.providerData[0].providerId;
In my case, we use only 2 providers google & password.
I get the user from "onAuthStateChanged" function as below:
import { fbAuth } from "./firebase";
fbAuth.onAuthStateChanged(function (user) {
if (user) {
console.log("authStateChanged:=====", user);
useItFurther(user);
} else {
console.log("authStateChanged: no user logged in.");
cleanUpAuthDetailsIfApplicable();
}
});
CAUTION: I haven't researched why is providerData an array and what more can be there in that array, what could be the sequence when there are more than 1 objects in that array, etc.
In my case, we had to add a condition for email validation based on provider. Like, for a particular domain email address, force user to use a specific provider.
I have been puzzled over this problem and couldnt find an appropriate solution for a long time as well. The solution turns out to be short:
String strProvider = FirebaseAuth.getInstance().
getAccessToken(false).getResult().getSignInProvider();
So, if (strProvider.equals("password")) then the authentication is by Email + Password,
if (strProvider.equals("google.com")) then the authentication is via Google,
if (strProvider.equals("facebook.com")) then the authentication is via Facebook.
Addition
However, with this one-liner you can get an exception wchich can be prevented by adding OnSuccessListener like so:
mAuth = FirebaseAuth.getInstance();
mAuth.getAccessToken(false).addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult getTokenResult) {
strProvider = getTokenResult.getSignInProvider();
}
});
Alternative
The getProviders() method list is sorted by the most recent provider
used to sign in. So the first element in getProviderData() is the
method that the user used to sign in.
Also the reason why
FirebaseAuth.getInstance().getCurrentUser().getProviderId() returns
firebase is because a backing Firebase Account is always created
irrespective of how the user signs in. This helps with linking and
unlinking of accounts where users may want to attach more than one
credential (however your view of the FirebaseUser has not changed).
as mention this post.

Categories

Resources