Firebase rules - allow users access only their own data - java

I am making a firebase notes app for android as a project (I'm a beginner and never done anything like this). My problem at the moment is that every note made, no matter what user made it, is available for every user to see. I want to make it that every user can only read/write their own notes but all the rules I found and tried so far made it that no notes are sent to the database at all or are not shown. I tried playing around with the code itself as well, but everything I did made the app crash but I'm open to suggestions.
The problem (logged in as user 2) :
The database with two users and the two notes:
I've tried solutions from other similar posts like this:
"rules": {
"tasks": {
"$userId": {
".read": "auth.uid == $userId",
".write": "auth.uid == $userId",
}
}
}
}
But none worked properly so far.
User creation:
fAuth.createUserWithEmailAndPassword(email,password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
fUserDatabase.child(fAuth.getCurrentUser().getUid()).child("basic").child("name")
.setValue(name).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
progressDialog.dismiss();
Intent mainIntent = new Intent(RegisterActivity.this, MainActivity.class);
startActivity(mainIntent);
finish();
Toast.makeText(RegisterActivity.this, "User Created", Toast.LENGTH_SHORT);
} else {
progressDialog.dismiss();
Toast.makeText(RegisterActivity.this, "Error: " + task.getException().getMessage(), Toast.LENGTH_SHORT);
}
}
});
} else {
progressDialog.dismiss();
Toast.makeText(RegisterActivity.this, "Error: " + task.getException().getMessage(), Toast.LENGTH_SHORT);
}
}
});
Note Creation:
(Not on the same page)
editText = findViewById(R.id.etx);
etd = findViewById(R.id.etdx);
button = findViewById(R.id.btnx);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("posts").push();
Map<String, Object> map = new HashMap<>();
map.put("id", databaseReference.getKey());
map.put("title", editText.getText().toString());
map.put("desc", etd.getText().toString());
databaseReference.setValue(map);
Intent back = new Intent(NewNoteActivity.this, MainActivity.class);
startActivity(back);
}
});
I would like to make it so that every user can only see, edit and delete notes that they made themselves.

I found a solution that might be useful to anyone else who's having troubles with this kind of thing.
I changed the path in the note creation to this:
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("posts").push();
Then changed the part of the code that shows the notes accordingly.
This is how it was:
Query query = FirebaseDatabase.getInstance()
.getReference()
.child("posts");
This is how it is now:
Query query = FirebaseDatabase.getInstance()
.getReference()
.child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("posts");

You cannot keep data as relational database. because firebase database is flat heierarchy. when you design the database use always
/users/user-id/posts
+ item1
+ item2
this is the way you must implement. thanks.

Related

Firebase authentication with onCompleteListener not working

I am trying to implement a basic android authentication system with Firebase. The following is my code:
Toast.makeText(MainActivity.this, "BEFORE ON-COMPLETE", Toast.LENGTH_SHORT).show(); //this shows up
mAuth.signInWithEmailAndPassword(emailInputted, passInputted).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Toast.makeText(MainActivity.this, "in OnComplete", Toast.LENGTH_SHORT).show(); //this does not show up
if(task.isSuccessful()){
Intent i = new Intent(MainActivity.this, Dashboard.class);
i.putExtra("EMAIL", emailInputted);
Toast.makeText(MainActivity.this, "Login Successful", Toast.LENGTH_SHORT).show();
startActivity(i);
} else {
Toast.makeText(MainActivity.this, "Please check email/password", Toast.LENGTH_SHORT).show();
}
}
});
Toast.makeText(MainActivity.this, "after OnComplete", Toast.LENGTH_SHORT).show(); //this shows up
I took this code from the official Firebase docs, but for some reason it is not working. The onComplete method is not being called for some reason. I can see the "before on-Complete" and "after on-complete" toasts, but not the "in OnComplete" one. I have added the users in my authentication table in the firebase console, and I am positive that I am entering the correct password.
After using the debugger, I saw that my code just skips over the onComplete() method, and does not even get to the isSuccessful() method. How do I fix this?
Thanks in advance!
Follow this, I hop you will get your answer.
lBut = findViewById(R.id.log_btn);
lBut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String a = usrn.getText().toString().trim();
String b = pswd.getText().toString().trim();
if (TextUtils.isEmpty(a) && TextUtils.isEmpty(b)) {
usrn.setError("Empty Email !!");
pswd.setError("Empty Password !!");
lBut.setClickable(false);
} else {
lBut.setClickable(true);
mAuth.signInWithEmailAndPassword(a, b).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#SuppressLint("ShowToast")
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Toast.makeText(getApplicationContext(), "Successfully Login!!", Toast.LENGTH_SHORT).show();
Intent i = new Intent(getApplicationContext(), HomeP.class);
ProgressBar prb = findViewById(R.id.progressBar);
prb.setVisibility(View.VISIBLE);
startActivity(i);
finish();
} else
Toast.makeText(getApplicationContext(), "Check Error And Try Again!!", Toast.LENGTH_SHORT).show();
}
});
}
}
});
This is the Event on the Login Button, you have more doubt then reply me.
So it fixed itself after I restarted my computer. No code changes. Maybe Android Studio was being wacky.

Firebase not adding class to Real Time Data Base

I'm using Firebase for login, but I want the user to also be added to the Real-Time Database so I can link the User with further data.
When a user registers, I can see that the user has been added to the authentication section of Firebase, and the user is able to log in but it does not get added to the Real-Time Database.
Here is my code
db = FirebaseDatabase.getInstance();
users = db.getReference("Users");
User user = new User();
user.setEmail((editEmail.getText().toString()));
user.setPassword((editPass.getText().toString()));
user.setName((editName.getText().toString()));
user.setPhone(editPhone.getText().toString());
users.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.setValue(user)
I also set an onSuccess/fail listener with Toasts letting me know if it failed or not. And neither of the toasts execute. I do have read/write enabled on my DB.
Define Variable.
private DatabaseReference userRef;
in OnCreate method.
userRef = FirebaseDatabase.getInstance().getReference().child("Users");
in a signup button
String getUserName = usernameEditText.getText.toString;
String getBio = bioEditText.getText.toString;
//and you should use the validates
if(getUserName.equals("")
{
usernameEditText.setError("Field Required");
}
else if(getBio.equals("")
{
bioEditText.setError("Field Required");
}
else
{
final HashMap<String , Object> profileMap = new HashMap<>();
profileMap.put("uid" , FirebaseAuth.getInstance().getCurrentUser().getUid());
profileMap.put("name" , getUserName);
profileMap.put("status" , getBio);
userRef.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.updateChildren(profileMap)
.addOnCompleteListener(new OnCompleteListener<Void>()
{
#Override
public void onComplete(#NonNull Task<Void> task)
{
if (task.isSuccessful())
{
Log.v("Profile Updated" , "Profile Updated Successfully");
Intent intent = new Intent(SettingsActivity.this , MainActivity.class);
startActivity(intent);
finish();
progressDialog.dismiss();
Toast.makeText(SettingsActivity.this, "Your profile Info has been updated", Toast.LENGTH_SHORT).show();
}
}
});
}

Is there a way to edit username and email via code in android studio?

I was wondering if there was a way to edit the name and email via code in Android Studio, to change it in Cloud Firestore. I made a program where it only changes the name in real-time and when logged off, and logged in again it changes back to the previous one which is in Cloud Firestore.
vardas is an EditText field in the app design.
My code:
public void updateProfile(final View view) {
view.setEnabled(false);
vardas1 = vardas.getText().toString();
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
UserProfileChangeRequest request = new UserProfileChangeRequest.Builder()
.setDisplayName(vardas1)
.build();
firebaseUser.updateProfile(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
view.setEnabled(true);
Toast.makeText(Profile.this, "SÄ—kmingai atnaujintas profilis", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
view.setEnabled(true);
Log.e(TAG, "onFailure: ", e.getCause());
}
});
}
I was wondering if there was a way to edit the name and email via code in Android Studio, to change it in Cloud Firestore.
Yes, there is. According to the official documentation regarding how to update a document in Cloud Firestore:
To update some fields of a document without overwriting the entire document, use the update() method.
Assuming you want to update the name and email of the authenticated user that exists at the following reference:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference usersRef = rootRef.collection("users");
DocumentReference uidRef = usersRef.document(uid);
Try the following lines of code:
uidRef.update(
"name", "John",
"email", "john#email.com"
).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully updated!");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error updating document", e);
}
});
The result of using this is code, is the update of the name property with "John".
What you are doing in your code is nothing else than updating the name in the FirebaseUser object. That operation is not related in any way with Firestore. So updating the FirebaseUser it doesn't mean that the user will be also updated in the Firestore database. There are two different separate operations that are not related.

Android: How to login an user with his phone number

I'm creating an app with where I want already signed up users to be redirected to their profile if their phone number is registered in the app, but if he is a new user then he will be redirected to the welcome page.
The problem is, I'm able to get the verification code for the phone number, but it is always redirecting to the welcome page irrespective of whether it is an existing user or a new user.
By far I've implemented Firebase phone authentication, and
private void verifyCode(String code) {
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithCredentials(phoneAuthCredential);
}
private void signInWithCredentials(PhoneAuthCredential phoneAuthCredential) {
mAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
final String num = getIntent().getStringExtra("phoneNumber");
final Query query = FirebaseDatabase.getInstance().getReference("users").orderByChild("birthday").equalTo(num);
if (task.isSuccessful()) {
if (num.equals(query.toString())) {
Log.i("Method", "Inside if block");
Log.i("value", num + query.toString());
Intent intent = new Intent(VerifyPhoneActivity.this, WelcomeActivity.class);
startActivity(intent);
} else {
Log.i("Method", "Inside if block2");
Log.i("value", num + query.toString());
Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
} else {
Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
I'm taking only the phone number of the user to log in/sign up, and there's no separate button for login and sign up. It should work in such a way that, once the OTP sent by the Firebase auth has been verified and if the phone number is already present in the database, it should directly fo the user's profile page instead of the welcome screen, however, if the phone number is new it should go to the welcome page instead of the user profile page.
The challenge I'm facing is I'm not able to check if the number entered is already present in the database in the user table(I've created a separate user table to store the details of the user when he signs up for the first time).
from what I understood you're able to register users but you're not able to redirect unregistered users to the login/signup screen, if that's the case then you can try to use
FirebaseAuth.getInstance().currentUser
in the on create view method in your welcome/home page it should be something like this
override fun onCreate(savedInstanceState: Bundle?) {
if (FirebaseAuth.getInstance().currentUser == null) {
startActivity(Intent(applicationContext, RegistrationActivity::class.java))
finish()
return
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// do your other stuff
}
this code is written in kotlin it shouldn't be that different in java. hope it helps.
UPDATE------
you have a few mistakes with your code, try this
private void verifyCode(String code) {
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(verificationId, code);
FirebaseAuth.getInstance().signInWithCredential(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
final String num = getIntent().getStringExtra("phoneNumber");
FirebaseDatabase.getInstance().getReference("users").orderByChild("phoneNumber").equalTo(num).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
} else {
FirebaseDatabase.getInstance().getReference("users").push().child("phoneNumber").setValue(num).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Intent intent = new Intent(VerifyPhoneActivity.this, WelcomeActivity.class);
startActivity(intent);
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
} else {
Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
first of all, you have to log in the user with the phone detail then you check if you have any user registered with that phone number if you do then that means that this user has already entered the app but if you didn't have any information of user with that phone number then you create his information and direct him to the welcome screen after creating his information, so if this user comes back again you will check if we have a record of user with that phone number (which we do) so this time he will be directed to the profile screen

Android Firebase Removing all user data with relogin

I'm trying to create users table in Firebase realtime database.However, everytime user re-logins his previously entered data is being removed or overwritten.Couldnt understand how should I change it.
private void firebaseAuthWithGoogle(GoogleSignInAccount account) {
AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Toast.makeText(RegisterActivity.this,"Registration Is Succesfull",Toast.LENGTH_LONG).show();
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user=mAuth.getCurrentUser();
final String databaseUserName=user.getDisplayName();
String name=mAuth.getCurrentUser().getDisplayName();
DatabaseReference myRootRef = FirebaseDatabase.getInstance().getReference().child("Users");
DatabaseReference userNameRef = myRootRef.child(databaseUserName);
//after that user is redirected to the main account activity.
Intent accountIntent = new Intent(RegisterActivity.this,UserAccountActivity.class);
accountIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(accountIntent);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(RegisterActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
// if signing up task is unsuccesfull we do make a error indication pretty much.
FirebaseAuthException e = (FirebaseAuthException )task.getException();
Toast.makeText(RegisterActivity.this, "Failed Registration: "+e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("LoginActivity", "Failed Registration", e);
}
}
});
}
So once I run the code, for the very first time it works perfectly fine and say I edit&add additional user info but once the user logs out and re-enters, everything is cleared out and node is again created.
Here you are saving the data in the database:
DatabaseReference myRootRef = FirebaseDatabase.getInstance().getReference().child("Users");
DatabaseReference userNameRef = myRootRef.child(databaseUserName);
Now on the second time, the data is not getting deleted or cleared, it cannot be deleted magically, the data is getting overridden. All you need to do is add a push() that will create a random id for each log in.
So like this:
DatabaseReference myRootRef = FirebaseDatabase.getInstance().getReference().child("Users").push();
DatabaseReference userNameRef = myRootRef.child(databaseUserName)
Edit:
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(Activity_name_here.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Toast.makeText(getApplicationContext(), "createUserWithEmail:onComplete:" + task.isSuccessful(), Toast.LENGTH_SHORT).show();
// 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(getApplicationContext(), "Authentication failed." + task.getException(),
Toast.LENGTH_SHORT).show();
} else {
Here is the working version of the source code. You can use the very same code for facebook login as well. This code simply prevents user from overwriting when logging in again.
private void firebaseAuthWithGoogle(GoogleSignInAccount account) {
AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Toast.makeText(SignInActivity.this,"Registration Is Succesfull",Toast.LENGTH_LONG).show();
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
//getting current users account
FirebaseUser user=mAuth.getCurrentUser();
//getting the display name of the current user to store them in our real time database
final String databaseUserName=user.getDisplayName();
//creating a child called users
final DatabaseReference myRootRef = FirebaseDatabase.getInstance().getReference().child("Users");
//here we make a control such that, if logged in user is exist in the realtime database
//if not exists, then we save them , if exists we continue with the else statement and break it.
myRootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.hasChild(databaseUserName)){
DatabaseReference userNameRef = myRootRef.child(databaseUserName);
//value is also set to user display name however it doenst have to be so
userNameRef.setValue(databaseUserName);
} else{
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//after that user is redirected to the main account activity.
Intent accountIntent = new Intent(SignInActivity.this,UserAccountActivity.class);
accountIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(accountIntent);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(SignInActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
// if signing up task is unsuccesfull we do make a error indication pretty much.
FirebaseAuthException e = (FirebaseAuthException )task.getException();
Toast.makeText(SignInActivity.this, "Failed Registration: "+e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("LoginActivity", "Failed Registration", e);
}
}
});
}

Categories

Resources