I am working on a project and I followed few tutorials in order to learn and build the app. But all of them, they change the Firebase read and write rules to true which is not safe. for example they change
{
"rules": {
".read": false,
".write": false
}
}
to
{
"rules": {
".read": true,
".write": true
}
}
This gives access to anyone to read and write the server data which is not safe in any way. And hence I turned this to false and now I am unable to register the user to Firebase it is giving an error saying 'Permission Denied. So what would I have to do in order to get the permission now.
Previously I was using this code to register the user to Firebase which is not working now.
mFirebaseAuth.createUserWithEmailAndPassword(editEmail.getText().toString(), editPass.getText().toString()).addOnSuccessListener(new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(AuthResult authResult) {
//Saving User to Database
User user = new User();
user.setEmail(editEmail.getText().toString());
user.setName(editName.getText().toString());
user.setPassword(editPass.getText().toString());
user.setPhone(editPhone.getText().toString());
users.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).setValue(user).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
waitingdialog.dismiss();
Snackbar.make(rootLayout, "Registration Successful", Snackbar.LENGTH_SHORT).show();
return;
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
waitingdialog.dismiss();
Snackbar.make(rootLayout, "Registration Failed" + e.getMessage(), Snackbar.LENGTH_LONG).show();
return;
}
});
}
});
There are different rules for in the Firebase for this reason and the registration of the user to Database depends on those rules for instance there are four rules given by Firebase
as Default
The default rules require Authentication. They allow full read and write access to authenticated users of your app only. They are useful if you want data open to all users of your app but don't want it open to the world
// These rules require authentication
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
as Public
During development, you can use the public rules in place of the default rules to set your files publicly readable and writable. This can be useful for prototyping, as you can get started without setting up Authentication. This level of access means anyone can read or write to your database. You should configure more secure rules before launching your app.
// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
"rules": {
".read": true,
".write": true
}
}
as User
Here's an example of a rule that gives each authenticated user a personal node at /users/$user_id where $user_id is the ID of the user obtained through Authentication. This is a common scenario for any apps that have data private to a user.
// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
as Private
Private rules disable read and write access to your database by users. With these rules, you can only access the database through the Firebase console.
// These rules don't allow anyone read or write access to your database
{
"rules": {
".read": false,
".write": false
}
}
For registering the user to Database while read and write permissions as false will only give permission to you to edit and read the data from the Firebase Console.
Related
I previously asked question to secure realtime database on firebase. I am only using Firebase Realtime database just to creating chat app. The user verification working separately on our own server and we are not using any firebase auth service for user verification. As Frank van Puffelen suggested few official docs. I am now generating JWT to authorize as per documentation but as we are not using any other services of firebase i am not sure how to authorized the real time database with generated JWT.
mAuth.signInWithCustomToken(mCustomToken)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success
} else {
// If sign in fails
Toast.makeText(CustomAuthActivity.this, "Authentication failed.",
}
}
});
How to validate users and secure our database from world so only app user can edit their node only.
I followed this guide for
Authenticate with Firebase with custom token.
User login with their credential
Server generate custom token (JWT).
Pass it to signInWithCustomToken as per doc.
What after that? guide bit incomplete for my use case.
Edit: The process:
Server generates JWT With PHP Firebase JWT
$Token = JWT::encode($request_data,$secret_Key,'HS512');
this token return back to app if user login successfully.
After successfully user login i call sign in with custom token i received from server with firebase
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isComplete()){
Intent intent=new Intent(getActivity(),MainActivity.class);
getActivity().startActivity(intent);
}
}
});
When user click chat button. Check if room already exist or not if not then create one for 2 users with their phone numbers like 9810012345-9810012346
DatabaseReference db = rebaseDatabase.getInstance().getReference();
db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(RoomTitle)) {
RoomName(RoomTitle, true);
}else {
RoomName(RoomTitle, false);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
public void RoomName(String Name, boolean RoomExist) {
button_SendMessage.setEnabled(true);
if (!RoomExist) {
Log.d(TAG, "Room Not Exist Creating One);
RoomName.put(Name, "");
FireBaseDatabase.updateChildren(RoomName);
}
// Launch Chat Screen
}
Then on chat screen i add items like linked question database structure
databaseReference = FirebaseDatabase.getInstance().getReference().child(Room_Name);
So creating room,allow reading writing message only created room, block access if room doesn't belong to users. I need to set rules for Realtime Database and only app users can access their rooms not others even they are app users(Block others app users to sneak into others users rooms) Below is the sample of our Realtime Database structure for better understanding how our 2 user room look like. I am not sure there is much thing to do on app side, i feel it's more database than app code question.
Once your call to signInWithCustomToken succeeds, that token will be passed to the server whenever the Firebase Realtime Database SDK connects, and from there on it will be verified and passed on to the auth variable in your security rules.
So you can check if the request comes from a signed-in user with:
{
"rules": {
".read": "auth.uid !== null",
".write": "auth.uid !== null"
}
}
That's a bit broad though, so you'll want to narrow it down to specific users. For example, you can allow only a content-owner access with:
{
"rules": {
"some_path": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "auth !== null && auth.uid === $uid",
".write": "auth !== null && auth.uid === $uid"
}
}
}
}
Any claims you have in your JWT will be available in the security rules too under the auth.token variable.
I want to make public only username node under the user's child for register function. So this value must be able for the non-register users to register. Here is my firebase database structure. How can I do that?
Here are my rules
{
"rules": {
"Homeland": {
".indexOn": ["username","email","bakiyetl","yarismada","yarismadabb","splashmesaj","uygulama1tut","uygulama2tut","uygulama3tut","uygulama4tut","uygulama5tut","uygulama6tut","uygulama7tut","uygulama8tut","uygulama9tut","uygulama10tut"]
},
"gunluksifreler": {
".read": true, // <-- allows every person
".write": true
},
".read": "auth !== null", // <-- allows read if logged in
".write": "auth !== null" // <-- allows write if logged in
}
}
Search username in database.
Query cmyquery = refbir.child("Homeland").orderByChild("username").equalTo(usergivenname);
cmyquery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
//user name already taken
}else if(sonlandirgorev!=1){
//You can use this username
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Rule 1: To be able to read a location or run a query on a location, you must be able to read the data on that location.
Knowing that, let's look at:
Query cmyquery = refbir.child("Homeland").orderByChild("username")
This code requires that the user can read the Homeland node.
This is referred to in the documentation as rules are not filters.
Rule 2: One you can read or write data at a certain level in the JSON, that permission can't be taken away at a lower level.
Given this rule and the above: one a user can read and query Homeland they can read all data below it. There is no way to hide part of that data.
This is referred to in the documentation as read and write rules cascade.
The common solution is to split the data by who needs access to it. So if you want only the username property values to be available to everyone, create a top-level usernames node with the same keys under it, and then just the value of that user's user name. On that new usernames node you can then grant more liberal access than on the Homeland node.
I want to allow access to user name so other users can search uses by user name.
I have a search feature in my app that allows users to search other users by user name
Firebase Rules
{
"rules": {
"users": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "auth.uid != null",
".write": "auth.uid != null"
},
},
"chat": {
"messages": {
".write": true,
".read": true
}
},
"app-settings": {
".write": true,
".read": true
}
}
}
Here is JSON of users
{
"app-settings" : {
"app-Available" : true,
"version" : "4"
},
"users" : {
"uid" : {
"blocked" : false,
"email" : "gamatiaihab#gmail.com",
"profilePhotoUrl" : "https://lh3.googleusercontent.com/a-/AOh14Gi6eXrdLfZTQH0B7GvoTxhqBHoVFUUTibK3QKfrfA=s96-c",
"uid" : "uid",
"userName" : "gamatiaihab"
}
}
}
In my app, I have a feature that allows the user to change their user name, with this code I'm trying to validate the user name checking if it's takin by other user or not, I normally access the single user information by child(uid) this works if firebase rules are only configured for authenticated users, but in order to validate the user name I need to not order the by child(uid) because this will return only single user which is the current user, I need to order by child("users").orderBy("userName") and this returns PERMISSION DENIED because i didn't pass child(uid)
private void validateUserName(final String chatUserName){
mEditChatUserNamePr.setVisibility(View.VISIBLE);
mDb = FirebaseDatabase.getInstance().getReference().child("users");
mDb.orderByChild("userName")
.equalTo(chatUserName)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null){
mEditChatUserNamePr.setVisibility(View.GONE);
mChatUserNameInput.setError("User name not available");
}else {
updateUserName(chatUserName);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
A user either has access to a complete node, or they don't have access to the node at all. There is no way to give them access to only part of each child node.
But even if there was, your code would have a problem: another user might claim the name between the time your query runs, and you write the new user name to the database. And while this problem may seem unlikely in your testing, in a successful app this type of problems (known as a race condition) is guaranteed to come back and bite you.
The idiomatic (and only guaranteed) way to implement uniqueness is to use the thing that must be unique as the keys in a list in the database. So in your case that'd mean:
Creating a list of usernames, with each key being a username, and each value being the UID of the user with that username.
Using a transaction to ensure no two users are updating the same node at the same time.
Using security rules to ensure that nobody can overwrite a username that was claimed by another user already.
Instead of repeating more here, let me point you to some previous questions where this was covered in more detail:
Firebase android : make username unique
How do you prevent duplicate user properties in Firebase?
unique property in Firebase
Firebase security rules to check unique value of a child #AskFirebase
I want to scan the database while registering a user to check if a certain 'username' is available or taken. I tried it using this code:
mDatabase.child("usernames").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
if (dataSnapshot.getValue() == uniqueUserName.getText().toString()) {
Snackbar snackbar = Snackbar
.make(coordinatorLayout, "Username already taken", Snackbar.LENGTH_SHORT);
snackbar.show();
} else {
signingUpMethod();
}
} else {
signingUpMethod();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But got this log:
W/SyncTree: Listen at /unique-usernames failed: DatabaseError: Permission denied
After doing some research, I find this answer: Firebase Permission denied Error
Current security rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Though it solves the problem, this solution might be handy in testing phase, but what when publishing the app? Please let me know.
For this situation you can simply extend your current rules to give everyone read access to your usernames like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"unique-usernames": {
".read": true
}
}
}
Also check out the security docs for more info because you can do a lot more with your security rules.
In Firebase best way is to Authorize the using Firebase Authentication and the for getting the list of users you have to maintain your database when user is registered successully using its Firebase Realtime Database. You can't maintain Authorized user list using only Firebase Realtime Database
I'm make an app with a Firebase Auth, but when I delete or when I disable an account I need make a signOut() manually (I control this with a user reload), if I don't, the user can keep uploading data. How I can fix this without the app code?
Firebase rules
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
App Code - How I detect it
if(user != null) user.reload().addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if(!task.isSuccessful()) {
String exc = task.getException().getMessage();
Log.e("FireBaseUser", exc);
auth.signOut();
}
}
});
When a token is minted, it gets an expiration timestamp. This essentially says: "the information in this token is valid until ...". Deleting the user does not invalidate any existing tokens.
Keep in mind that since the newest Firebase Authentication SDKs, the tokens are only valid for one hour. So after at most an hour, the token will expire and it will be impossible for the deleted user to refresh it.
If this is not enough for your application, you can add logic to your application that marks the deleted users in the database (in a section that only the administrator can access):
/deletedUsers
209103: true
37370493: true
You can then in your security rules validate that only non-deleted users can access data:
".read": "!root.child('deletedUsers').child(auth.uid).exists()"