Make username unique with rules [duplicate] - java

Parse will shut down at the end of the year, so I decided to start using Firebase. I need to implement a register process with 3 fields : email, username, password (Email & username must be unique for my app).
Since, Firebase is not providing an easy way to manage username like Parse, I decided to use only the email/password registration and save some additional data like username. Here is my users data structure :
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
}
}
But, what I want to do is to make the username unique and to check it before creating an account.
These are my rules :
{
"rules": {
".read": true,
".write": true,
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {".validate": "!root.child('users').child(newData.child('username').val()).exists()"}
}
}
}
}
Thank you very much for your help

Part of the answer is to store an index of usernames, that you check against in your security rules:
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
So the usernames node maps a username to a uid. It essentially reads as "username 'myname' is owned by 'some-user-uid'".
With this data structure, your security rules can check if there is already an entry for a given username:
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
This validates that the username isn't claimed by anyone yet OR it is claimed by the current user.

Save usernames as suggested by Frank but when you save usernames, use runTransaction function in Firebase to make sure that the username is not taken. This function is guaranteed by Firebase to be an atomic operation so you can be rest assured of no collision
firebaseRef.child("usernames").child(username).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.getValue() == null) {
mutableData.setValue(authData.getUid());
return Transaction.success(mutableData);
}
return Transaction.abort();
}
#Override
public void onComplete(FirebaseError firebaseError, boolean commited, DataSnapshot dataSnapshot) {
if (commited) {
// username saved
} else {
// username exists
}
}
});

make a new branch for username and when new user login get list of all username and check wether it is present in db or not if it is present show them toast otherwise put its username in the username branch ..

I dont know much about firebase security yet, but I may have solved the problem using Java. I have posted it below.
my data structure is
myapp
{
users: {
<unique generated-id>
{ username: "example.username" }
}
}
public boolean isUsernameExists(final String enteredUsername) {
final Boolean[] isExist = {false};
FBref.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
String existingUsername = (String) userSnapshot.child("userName").getValue();
if (existingUsername.equals(enteredUsername)) {
isExist[0] = true;
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
//some error thrown here
}
});
return isExist[0];
}

Related

Firebase Realtime Database Rules Permission Denied

I have an app where I am using a custom authentication method. First, on the user login into the app, I generate the JWT Token on the server and send it back to the app.
function generateJWT($con,$userID,$cretedTime) {
$secret_Key = "-----BEGIN PRIVATE KEY-----\HCPW\nAtY9K1/19yScEhdmhw8Ozek=\n-----END PRIVATE KEY-----\n";
$date = time();
//$expire_at = $date->modify('+3 minutes')->getTimestamp(); // Add 60 seconds
$domainName = "firebase-adminsdk-XXXXXXXX.iam.gserviceaccount.com";
$request_data = [
'iss' => $domainName,
'sub' => $domainName,
'aud' => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
'iat' => $date, // Issued at: time when the token was generated
// Issuer
//'exp' => $date+(60*60), // Maximum expiration time six month in seconds //15778476
'uid' => $userID, // User name
'created' => $cretedTime, // User name
];
$newToken = JWT::encode($request_data,$secret_Key,'RS256');
return $newToken;
}
Then In the app send on receiving this token I am start the login process.my app using custom firebase auth
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isComplete()){
if(getActivity()!=null){
//User Logged In Successfully
}else{
// Failed
}
}
}
});
So after days of googling, I got the Firebase rules for the structure of my database to look like this
{
"Chat": {
"206-4": "",
"311-158": "",
"215-112": "",
"734-115": "",
"734-55": "",
"734-468": "",
"34-32": "",
"534-179": "",
"734-345": {
"-NI7hqW3YTFKpnSZU422": {
"Message": "Test",
"Message_From": "Support ",
"Message_From_ID": "4",
"Message_To": "Demo",
},
"-NMVOwlAqmyIA52QU9F-": {
"Message": "Hi",
"Message_From": "Support ",
"Message_From_ID": "4",
"Message_To": "Demo",
}
},
"347-234": {
"-NI7hXybU02Mg6vYqdKp": {
"Message": "Ohio",
"Message_From": "Elaxer Support ",
"Message_From_ID": "4",
"Message_To": "Demo 2",
}
},
"281-69": "",
"317-34": ""
},
"Users": {
"4": {
"Online": false,
"lastSeen": "1675785660782"
},
"284": {
"Online": false,
"lastSeen": "1673611185873"
}
},
"UsersLocations": {
"4-210": {
"-1": {
"Latitude": "22.605",
"Longitude": "88.375"
}
},
"25-21": {
"-1": {
"Latitude": "22.605",
"Longitude": "88.375"
}
}
}
}
Firebase Rules
{
"rules": {
"Chat": {
"$room_id": {
".read": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)",
".write": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)"
}
},
"Users": {
"$uid": {
".write": "$uid === auth.uid"
}
},
"UsersLocations": {
"$user_location_id": {
".read": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')",
".write": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')"
}
}
}
}
So when ever i tried to create or get the Chat node (Chatroom).
DatabaseReference db = FirebaseDatabase.getInstance().getReference().child("Chat");
db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
It gives me error
Listen at /Chat failed: DatabaseError: Permission denied
I am not able to understand why i am getting this error when i am only checking the user id exist in room name and my jwt token on generation time having user id of one user. Please help me out, what's wrong i am doing with my rules
As Alex also answered: you're not granting anyone read access to /Chat, so any code trying to read from there will be rejected. I would not recommend his answer though, as the rule on /Chat makes the more strict rules on /Chat/$room_id meaningless.
I recommend reading the documentation on rules don't filter data (which explains why your current code don't work and on the fact that permissions cascade (which explains why the rules in Alex' answer make the ones you already have meaningless).
The data structure you have look like what I described in my answer to: Best way to manage Chat channels in Firebase. In my answer there I also showed how to model security rules to allow read access and how to get a list of the chat room for the user, so I recommend checking that out.
As I tried explaining in the comments, the way you handle the sign-in result is wrong:
// ❌ THIS IS WRONG ❌
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isComplete()){
if(getActivity()!=null){
//User Logged In Successfully
}else{
// Failed
}
}
}
});
Instead, when a Task completes you should check whether it succeeded or failed, as shown in the documentation on handling tas results. In your case that'd be:
// ✅ Handling success and failure correctly 👍
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()!=null) {
// User signed in
// TODO: access database
} else {
// Sign in failed
throw task.getException();
}
}
});

How to check if user entered userid already exists in the firebase through java?

I've tried so many ways to check if a user id is already in the firebase but all method are in vain.
Below is my code to check if user id exists but whatever data I enter it does not show the required error.
userid = findViewById(R.id.userid);
String userVal = userid.getEditText(). getText().toString();
boolean userquery = FirebaseDatabase.getInstance().getReference().child("user").orderByChild("userid").equals(userVal);
if(userquery) {
userid.setError("This user name already exists");
return;
}
Whenever I try to add existing value in the input it accepts the value and overwrites in the database.
Here is the screenshot of my firebase database.
Your code so far only sets up a query. It doesn't actually execute the query, so there's no way it can detect whether the data exists. To execute a query, you have to attach a listener to it.
So in your case, that could be something like:
boolean userquery = FirebaseDatabase.getInstance().getReference().child("user").orderByChild("userid").equals(userVal);
userquery.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else if (task.getResult().exists()) {
userid.setError("This user name already exists");
}
}
});
On older SDK versions the equivalent would be:
boolean userquery = FirebaseDatabase.getInstance().getReference().child("user").orderByChild("userid").equals(userVal);
userquery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
userid.setError("This user name already exists");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}

Adding Data to firebase Realtime Database showing Error

I am making an app based on firebase Realtime database. I have set rules as given below:
{
// Allow anyone to read data, but only authenticated content owners can
// make changes to their data
"rules": {
"Users": {
"$uid": {
".read": true,
// or ".read": "auth.uid != null" for only authenticated users
".write": "auth.uid == $uid"
}
}
}
}
I am trying to add data by the following code in java. But getting Error.
myfire = FirebaseDatabase.getInstance();
myRef = myfire.getReference("Users").child("Some Authentic User");
//======================
btnAdmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stName = etName.getText().toString();
stRoll = etRoll.getText().toString();
etName.setText("");
etRoll.setText("");
myRef = myfire.getReference();
myRef.child("201").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
model model =new model();
model.setFb01name(stName);
model.setFb04roll(stRoll);
myRef.child("Basic").setValue(model);
Toast.makeText(getApplicationContext (),"Sorry",Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(getApplicationContext (),"Error",Toast.LENGTH_SHORT).show();
}
});
}
});
My model class goes like this:
public class model {
String fb01name;
String fb04roll;
public model() {
}
public model(String fb01name, String fb04roll) {
this.fb01name = fb01name;
this.fb04roll = fb04roll;
}
public String getFb01name() {
return fb01name;
}
public void setFb01name(String fb01name) {
this.fb01name = fb01name;
}
public String getFb04roll() {
return fb04roll;
}
public void setFb04roll(String fb04roll) {
this.fb04roll = fb04roll;
}
}
I cannot find the error. The logcat is blank.
I was successful in adding data previously .But after changing the rules it failed.I want the database path like this:
(Main Node)Users--
(first Child)---Authenitic User(As added by "Add User")
(second child)-----some id( like '201')
(Targeted Children) 1 ------fb01name (and its value)
2------fb04roll (and its value)
Can anybody will please guide me practically?
Since onCancelled is called on your listener, that means that you don't have permission to read the data that you're trying to access. If you log the databaseError.toException() that you get in onCancelled you should also see that, as it should tell you that permission was denied.
Distilling your code down, you're attaching a listener to:
myRef = myfire.getReference();
myRef.child("201").addValueEventListener(...
So that is the path /201 in the database, where indeed your rules don't grant anyone read access.
My best guess is that the myRef = myfire.getReference(); line is a mistake, and removing it will lead to reading /Users/Some Authentic User/201, which is probably what you want to do.

Login using email stored in firebase realtime database

I am developing an android App in which I implemented Firebase realtime database. And now I want to login using by email id and password which stored in database. I know firebase auth can be use for authentication but I want to login using realtime database like we do in Sql or my sql.
I done this. First I fetch data of particular email id from database and than compare its password and assign the related function to do..
Query query = databaseReference.child("users").orderByChild("email").equalTo(txvUsername.getText().toString().trim());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// dataSnapshot is the "issue" node with all children with id 0
for (DataSnapshot user : dataSnapshot.getChildren()) {
// do something with the individual "issues"
UsersBean usersBean = user.getValue(UsersBean.class);
if (usersBean.password.equals(txvPassword.getText().toString().trim())) {
Intent intent = new Intent(context, MainActivity.class);
startActivity(intent);
} else {
Toast.makeText(context, "Password is wrong", Toast.LENGTH_LONG).show();
}
}
} else {
Toast.makeText(context, "User not found", Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Thanks..
I am assuming that you know how to use ValueEventListener, onDataChange method and Datasnapshot.
As you stated above, you want to do it like in SQL or MySQL. So instead of using email, I will use username and password.
You can authenticate your user without using Firebase Authentication. However, this is not recommended for the database security.
{
"userList": {
"JRHTHaIsjNPLXOQivY": {
"userName": "userA",
"password": "abc123"
},
"JRHTHaKuIFIhnj02kE": {
"userName": "userB",
"password": "abc123"
}
},
"userAuth": {
"userA": "JRHTHaIsjNPLXOQivY",
"userB": "JRHTHaKuIFIhnj02kE"
}
}
As you can see above I have saved the the userName and userId in firebase db. When a user enter the username and password, you should read the userId by using username in the userAuth. Once you have retrieved the userID, use it to access the userList for the user details. This is where you check for the entered password.
{
"userList": {
"userA": {
"password": "abc123"
},
"userB": {
"password": "abc123"
}
}
}
Or you can just authenticate your users like this.

Understanding if data exists in Firebase

I am using the following to fetch a username from Firebase. But in this case, how do I get to know, if the username exists or not.
Firebase firebaseRef = new Firebase("<Firebasae_URL>/Users");
Query query=firebaseRef.orderByChild("username").equalTo("username");
So in your case the code should be like this, to find an username exists in your database.
Firebase firebaseRef = new Firebase("<Firebasae_URL>/Users").child("username");
firebaseRef.addListenerForSingleValueEvent(new ValueEventListener) {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// User Exists
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
The solution I found to check if a query return value was
let docRef = db.collection('videos').select('id').where('id','==',quoteData.id);
docRef.get().then(function(doc){
if(doc.size > 0){
// return value
}
else{
// no return value
}
})

Categories