Firebase Realtime Database xxxx-xxxx-4458' has insecure rules - Followup Question - android

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.

Related

How to use firebase with read and write rules as false

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.

Creating anonymous user in Firebase doesn't work

I have been trying to solve this problem for more than 4 hours now, still nothing...
I want that only signed in users can write and read data to my database, so I decided to create anonymous user. I set firebase rules like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Now I have this code in my MainActivity:
mAuth = FirebaseAuth.getInstance();
mAuth.signInAnonymously().addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
System.out.print("NEW USER SIGNED IN?");
} else {
Log.d(TAG, "Fail");
}
}
});
The purpose of this code is to create an anonymous user, which can read and write into my database, but this code isn't even getting invoked.
Is this the correct way to create an anonymous user which may read and write data into my firebase database?
If you have followed these steps:
https://firebase.google.com/docs/auth/android/anonymous-auth
Added Firebase to your project, added the dependency and enabled the anonymous auth (which you do in the firebase auth console), this code should work.
If you use an emulator, make sure it has internet connection.

Add extra User Information with firebase

I have integrated firebase auth with my android app. Lets say a user has a mail abc#abc.com. I want to add some extra information to the user like the name of the user, occupation and address. How can i connect the user auth table with my android app to do that?
Do i need to write any APIs for that?
First, create a users directory in db. Then, using user's unique id you get from authn process, store the user info under users/{userid}.
To achieve this, you need to get into the details of Firebase database. See here: https://firebase.google.com/docs/database/android/save-data
You do not need to write any custom code to do this. Firebase already has features you can use.
The first thing you'd need to do is ensure that users have access to only the data they store. To do this, go to Database/Rules and change your rules to this:
{
"rules": {
"my_app_user": {
"$uid": {
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
}
}
}
Then, to save the new details in a Firebase database, do this:
MyAppUser user = new MyAppUser();
user.setAddressTwo("address_two");
user.setPhoneOne("phone_one");
...
mDatabaseReference.child("my_app_user").child(firebaseUser.getUid()).setValue(user).
addOnCompleteListener(DetailsCaptureActivity.this,
new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
...
The name of the child my_app_user must match both in your code and the Firebase rules else it won't persist.
If everything goes as is supposed to, you should see the details in your database:
You have to create another database table say "user". On successful signin, signup for first time you have to create a new row in user table.
public static void writeNewUser(DatabaseReference databaseReference, String userId, String name, String email, int accountType) {
User user = new User(name, email, accountType);
databaseReference.child("users").child(userId).setValue(user);
}
You may refer https://github.com/firebase/quickstart-android/tree/61f8eb53020e38b1fdc5aaeddac2379b25240f3b/database

Email verification using Firebase 3.0 on Android

I knew that we can verify the users email with Firebase Auth 3.0. I'm not able to find any documentation regarding email verification on Android. I'm able to find the same for iOS as well as web but not for Android. Any link to the documentation would be helpful.
From the image, it is clear that once the user signs in, he will be intimated regarding that on email to confirm his subscription. I've subscribed myself and also verified in the users section in Auth tab and I am able to see my mail id and firebase generated unique user id. What's missing here is the confirmation email to my email id. Did some one try this or am I too early trying this? Thanks for your help.
Email verification for android is now available in Firebase.
See this release note:
https://firebase.google.com/support/release-notes/android#9.6
Update
Email verification is available in version 9.6 and higher of the Firebase SDK for Android.
Original answer
Email verification is not available for Android yet. Also answered here with more context.
An alternative suggested by the Firebase team
One thing you could do is to add a node to your Firebase Database which contains all email addresses as children. You should make this node only publicly readable (via Firebase security rules).
Then from within your apps, once a user signs up / signs in, you check if the email of that user is on the list, and if not, you sign them out and kick them out of your app (and as a bonus, you could even log the intruder's email address in your database, so you can later check who is trying to access your app).
This will work for initial testing if you know the e-mail ids of the people who are gonna test your app until the e-mail verification makes its way to Android.
Since email verification only works with Email/Password authentication, the best place to send it wold be in the onComplete method of createUserWithEmailAndPassword(...) method, after signup is successful.
firebaseAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
sendVerificationEmail();
....
The custom sendVerification method is:
public void sendVerificationEmail() {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
user.sendEmailVerification()
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Toast.makeText(SignUpActivity.this, "Signup successful.
Verification email sent", Toast.LENGTH_SHORT).show();
}
}
});
}
}
You can then check if the user has verified their email anywhere in your app by calling:
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
firebaseUser = firebaseAuth.getCurrentUser();
if (firebaseUser != null ) {
Log.e(TAG, firebaseUser.isEmailVerified() ? "User is signed in and email is verified" : "Email is not verified");
} else {
Log.e(TAG, "onAuthStateChanged:signed_out");
}
}
};

Firebase deleted user is able to change data. How can I fix this without modifying application code?

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()"

Categories

Resources