I'm actually working on a social app. I want to create a friend system and I'm struggling with the user search.
I want the user to put the username of his friend in a TextField widget, query the Cloud Firestore (Firebase) database and get the friend's user profile if it exists.
The problem is, usernames are stored in "Users/USER_ID/profile/username" and I don't know how to query the database for this demand.
Here is the Cloud Firestore "path" i'm talking about : -
A little help would be welcome :)
I tried this but this returns null, probably because it is not searching in the right place.
dynamic sUserFromUsername(String username) async {
try {
await FirebaseFirestore.instance
.collection('Users')
.where('username', isEqualTo: username)
.get()
.then((value) => value);
} catch (exception) {
print(exception);
}
}
Related
I have a stream that fetches data from my Firestore database and orders them according to the latest message. The problem is that the stream does not order them correctly. So basically it fetches the data once, and if I add a new message, it is not shown on top. Any idea why?
Here is my query:
The Stream:
Stream<List<RoomsListModel>>? chats;
The query:
#override
void initState() {
chats = FirebaseFirestore.instance
.collection("rooms")
.where("users", arrayContains: userId)
.where("latestMessage", isNull: false)
.orderBy("latestMessage")
.orderBy("latestMessageTime", descending: true)
.snapshots()
.asyncMap((events) =>
Future.wait([for (var event in events.docs) generateRoom(event)]));
super.initState();
}
So apparently in Firestore, you cannot do a conditional query like "isNull" and then orderBy another value.
Yes, you can. If a field in Firestore holds the value of null, null being a supported data type, you can query against it. Besides that, you can also order the results by another field, if and only if you create an index.
Found a solution: So apparently in firestore you cannot do a conditional query like "isNull" and then orderBy another value. Therefore I found a workaround: I just changed the conditional query to where "latestMessageTime" is null and ordered by latest Message time.
In my firebase realtime database, only authenticated user can read data. Suppose a user(suppose xyz) is logged in in app and I reset his password. After resetting/changing password, xyz is still able to read/write in database until I reauthenticate and logged him out manually(through code). The problem is, if someone modify app source code and remove manually log-out part, how do I prevent that user from reading/writing to database?
You can do this by invoking a cloud function with the new password, I would advise encoding the password and decoding it but this is out of the scope of this question.
// declare function
exports.updatePass= functions.https.onCall((data, context) => {
return admin
.auth()
.updateUser(context.auth.uid, { password: 'newPassword' })
.then(async (userRecord) => {
const utcRevocationTimeSecs = await new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
return admin.database().ref('metadata/' + uid)
.set({ revokeTime: utcRevocationTimeSecs })})
.then(() => admin.auth().revokeRefreshTokens(context.auth.uid))
.then(() => return {message: "success"};
.catch((error) => {
return error;
});
});
This will write the changes to the realtime database of which you can listen to changes, and use within your Security Rules, This can easily swap this out for firestore if needed.
{
"rules": {
"metadata": {
"$user_id": {
// this could be false as it is only accessed from backend or rules.
".read": "$user_id === auth.uid",
".write": "false",
}
}
}
}
Source:
https://firebase.google.com/docs/auth/admin/manage-users#update_a_user
https://firebase.google.com/docs/auth/admin/manage-sessions
Firebase Authentication is based on tokens, the shorted lived (ID token) of which is valid for an hour. Changing the password on the account does not invalidate existing ID token(s), so if you want the user to lose access immediately, you'll have to do that in another way.
A common way is to keep a banlist/blocklist in the database, for the UIDs that are supposed to be blocked even if they have a valid ID token. This scenario is described more fully in the documentation on detecting ID token revocation.
With this approach though, you don't have a clear trigger on when to unblock/reenable the user's access, as there is no callback when their password changes. The best I can think of is to periodically run a Cloud Function to check if the user's password hash has changed, but I'm hoping somebody comes up with a better option for this step.
Given the following simplified Firebase Firestore database structure:
users
user1
email: "test1#test.com"
user2
email: "test2#test.com"
I want to be able to query if a user with a specific email exists in the database WITHOUT giving access to the whole users collection
Is it possible to achieve this using the database rules without modifying the database structure?
If it's not possible, what would be the best workaround for this?
I see two possible solutions, but it seems to me that they add too much complexity:
Expose that specific query via an API endpoint (maybe using Firebase Functions)
Modify the DB structure as suggested in this thread: Firestore security rules based on request query value
Which approach do you think is better?
To meet the requirement, "query if a user with a specific email exists in the database WITHOUT giving access to the whole users collection," you'll need to rethink your database architecture. Firestore won't allow you to make queries on data that you don't also have read access to.
I would recommend creating a separate collection that just contains email addresses in use like this:
{
"emails": {
"jane#example.com": { userId: "abc123" },
"sally#example.com": { userId: "xyz987" },
"joe#example.com": { userId: "lmn456" }
}
}
This collection should be updated everytime a user is added or an email address is changed.
Then set up your Firestore rules like this:
service cloud.firestore {
match /databases/{database}/documents {
match /emails/{email} {
// Allow world-readable access if the email is guessed
allow get: if true;
// Prevent anyone from getting a list of emails
allow list: if false;
}
}
}
With all of that you can then securely allow for anonymous queries to check if an email exists without opening your kimono, so to speak.
List All Emails
firebase.firestore().collection('emails').get()
.then((results) => console.error("Email listing succeeded!"))
.catch((error) => console.log("Permission denied, your emails are safe."));
Result: "Permission denied, your emails are safe."
Check if joe#example.com exists
firebase.firestore().collection('emails').doc('joe#example.com').get()
.then((node) => console.log({id: node.id, ...node.data()}))
.catch((error) => console.error(error));
Result: {"id": "joe#example.com": userId: "lmn456"}
Check if sam#example.com exists
firebase.firestore().collection('emails').doc('sam#example.com').get()
.then((node) => console.log("sam#example.com exists!"))
.catch((error) => console.log("sam#example.com not found!"));
Result: sam#example.com not found!
On the left my database schema, on the right is my react-native code.
I followed this documentation to get started with Realtime Database for Android.
I am trying to get the value "Lesson" from key "Category".
none of the commands I tried work. How I can get Lesson value?
I tried test.parent but I got the correct location in my database but I want the values.
thanks!
Documentation is your Friend!
The answer to your question is in the document you attached!
If you scroll a bit down there's a section One-time read
To read the value once, call the once method on a reference:
import database from '#react-native-firebase/database';
database()
.ref('/users/123')
.once('value')
.then(snapshot => {
console.log('User data: ', snapshot.val());
});
So just replace the ref path '/users/123' with your path 'Lesson 1/1 Introduction/Category'
You can also listen for changes in the document in real-time. Check out Real-Time Changes
import database from '#react-native-firebase/database';
database()
.ref('/users/123')
.on('value', snapshot => {
console.log('User data: ', snapshot.val());
});
I have used FirebaseAuth for Login purpose in application. While createUserWithEmailAndPassword I am creating the same user in FirebaseDatabase with some additional fields.The structre is
[{
"email":"abc#gmail.com",
"password":"xyz",
"name":"sachin",
"address":"Pune",
"contact":"1234567890"
},
{
"email":"pqr#gmail.com",
"password":"def",
"name":"Ashay",
"address":"Pune",
"contact":"1234577777"
}]
Suppose after successfull Login of user Sachin with email abc#gmail.com and password xyz I want the address and contact from database.How to get that values ?
For future reference if you want to get a certain information from DB using UID
//To get logged in user information
var user = firebase.auth().currentUser;
var uid = user.uid; //get the UID from the current user
According to me, you should use firebase uid as index for storing data. When user authenticated you will get user's uid, on this basis you can access user's data. I hope it will work.