O., this may be a relatively simple question, but it's one that I couldn't figure out (both by testing code and googling).
I have Simple Login with Email/Password enabled, and so when they login, the email used will be converted into a md5_hash.
I've created a "Users" object store, which currently looks like this:
users
|____ Bob
|__ md5_hash: 8120jf283
|__ username: Bob
|____ Alice
|__ md5_hash: 2938njv29
|__ username: Alice
|____ Chris
|__ md5_hash: 230948mv2
|__ username: Chris
My question is: How can I change the "schema" so that it stores the users by their md5_hash, instead of by their username? You can see above that it's storing the users by Bob, Alice, and Chris. It would work better for me if I could store them by their md5_hash so that it would like something like this:
Users
|___ 02938492
|__ md5_hash: 02938492
|__ username: Bob
Here's the code that I'm using to create a user and $save it into the Firebase database:
var ref = new Firebase(FIREBASE_URL + 'users');
var users = $firebase(ref);
function(authUser, username) {
users[username] = {
md5_hash: authUser.md5_hash,
username: username,
$priority: authUser.uid
};
users.$save(username).then(function() {
*do something here in callback*
});
I've tried 2 different things that didn't work. I tried using
users[authUser.md5_hash] = {...};
and I also tried
users.$save(authUser.md5_hash)
Neither of these work. Nothing gets saved to the Firebase database. It's perplexing to me because I'm almost 100% sure that it's one of those 2 parts that determines how the key is saved to firebase, but it doesn't do anything.
ULTIMATELY, what I'm trying to do is be able to "grab" the username value after a person logs in. I will be displaying their username on a navbar in a kind of "welcome Bob" fashion. However, when a user logs in, there is not yet any correlation between their md5_hash and their username. I have to be able to grab the username from the "users" table, and because I don't know their username, and the "users" table is currently indexed or keyed by their username, I have no way to get it...
A quick design question as well (if you have time):
Also, this doesn't have to do with the above question, but it's somewhat related. Just a quick question about designing my firebase "schema". If you have time, it'd be great if you could take a quick read below:
As I mentioned before, I'm using Simple Login with email/password. When a user logs in, I have access to the md5_hash of their email. Thus, to associate other user information with that login, I've created the "User table". I'm trying to connect the Login/Authentication with the said table using the md5_hash (because this is unique to each user), and then store other useful user data in it such as username, age, etc.
Is this how one would design their firebase schema? I'm used to using some sort of GUID or index in SQL, and I figured that since an md5_hash is unique, I could similarly use it to "index" my firebase users table. I just don't know if doing it this way will cause some problems in the future.
Thank you very much for your time. Any help or advice on this would be much appreciated! :)
With Simple Login, the user is created with a unique id:
var fbRef = new Firebase(URL);
var auth = new FirebaseSimpleLogin(fbRef);
auth.createUser(email, password, function(error, user) {
if( error ) throw error;
console.log('unique id', user.uid);
});
You can utilize that for your unique identifier. If that is insufficient, you can obtain a unique ID from the Firebase push() command:
var fbRef = new Firebase(URL);
var uniqueId = fbRef.push().name();
fbRef.child('users/'+fbRef).set(userData);
// or even shorter
var uniqueId = fbRef.push(userData).name();
With AngularFire, you can simply call $add instead of $save to create unique ids:
var users = $firebase(fbRef);
users.$add( userData ).then(function(ref) {
console.log('unique id', ref.name());
});
Related
I don't know if this is the way to ask this, also I want to achieve this without state management.
so here's the code that getting user is from firebase
final docUsers = FirebaseFirestore.instance.collection('users').doc();
final userObject = Users(
id: docUsers.id,
name: userName,
email: emailAdress,
password: password,
);
await docUsers.set(userObject.toJson());
userID = docUsers.id;
userID = docUsers.id; the user id is the global variable here, so I assigned the value to the global variable when the id received. but when it using its shows the value is null, how to achieve this without statemanagement (I meant bloc, provider and like many others. not the "state management").
so how can I achieve that?
Could you show how and when your value is NULL?
But this might help:
class MyGlobalVariables {
static String userId = '123';
}
MyGlobalVariables.userId = 'abc';
print( MyGlobalVariables.userId ); // = abc
It looks like you want to create entries for new users of your App in the database. If this is the case, one would want to use Firebase Authentication for dealing with that:
https://firebase.google.com/docs/auth/
In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.
I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.
But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.
Please, if you want to help me, give me an example in javascript using a firebase url/array.
Thank you!
A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.
You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:
var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
user1 = "Eusthace";
user2 = "Frank";
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:
"userChatrooms" : {
"Frank" : {
"Eusthace_Frank": true
},
"Eusthace" : {
"Eusthace_Frank": true
}
}
If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.
This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:
{
"rules": {
"chatrooms": {
"$chatroomid": {
".read": "
root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
"
}
}
}
}
In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.
Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).
We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.
Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.
Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.
import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)
In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.
I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.
But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.
Please, if you want to help me, give me an example in javascript using a firebase url/array.
Thank you!
A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.
You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:
var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
user1 = "Eusthace";
user2 = "Frank";
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:
"userChatrooms" : {
"Frank" : {
"Eusthace_Frank": true
},
"Eusthace" : {
"Eusthace_Frank": true
}
}
If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.
This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:
{
"rules": {
"chatrooms": {
"$chatroomid": {
".read": "
root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
"
}
}
}
}
In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.
Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).
We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.
Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.
Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.
import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)
Disclaimer, I am trying to self-teach myself development. I am building a hybrid mobile app using Ionic 1 and now Firebase 3 for my database and authentication.
For my scenario, in short, I'm trying to display a list of 'friends' for the user that is currently logged in. Here is the current data structure I have (the relevant part anyway):
Data Structure
I have a line of code that does return me what I want:
var friends = $firebaseArray(ref.child('users').child('-KXcxMXkKs46Xv4-JUgW').child('friends'));
Of course, that can't work because there is a nice little hard coded value in there.
So, I looked into how to retrieve the current UID so I could replace the hard coded value. But after running the following bit of code through, the first node under user is not the UID (it is some other auto generated value that I don't really know how it got there). The UID is actually within the id field.
var ref = firebase.database().ref();
authObj = $firebaseAuth();
var firebaseUser = authObj.$getAuth();
console.log(firebaseUser.uid);
So, ultimately what I would love is to be able to change the data structure so that the UID is the first node under Users, but I can't seem to find documentation to do that. I looked at this other stack thread, but it is for an outdated version and I can't seem to connect the dots. Other thread
Though, if I can't change the structure, I still need to figure out how to access that friends node for the current user, one way or another.
Thank you in advance. This is my first stackoverflow post, so be gentle.
Update:
Per Frank's comment, this is the code that I execute to create users - $add is what is creating the push id (-KXcxM...).
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email,
registered_in: Date()
// a number of other things
};
var messagesRef = $firebaseArray(firebase.database().ref().child("users"));
messagesRef.$add(profile);
},
register: function(user) {
return auth.$createUserWithEmailAndPassword(user.email, user.password)
.then(function(firebaseUser) {
console.log("User created with uid: " + firebaseUser.uid);
Auth.createProfile(firebaseUser.uid, user);
Utils.alertshow("Success!","Your user has been registered.");
})
.catch(function(error) {
Utils.alertshow("Error.","Some helpful error message.");
console.log("Error: " + error);
});
}
Instead of creating a $firebaseArray and calling $add on it, you can just store the user using the regular Firebase JavaScript SDK:
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email
};
firebase.database().ref().child("users").child(uid).set(profile);
}
Since AngularFire is built on top of the Firebase JavaScript SDK, the two interact nicely with each other. So if you have any existing $firebaseArray on users it will pick up the new profile too.
I am saving logged in userId with each record saved in my Meteor app collection as shown in the example below, yet I was wondering if there was any way in Meteor where I can retrieve user name based on the user saved id without have to make another query on the users collection? In Node.js / mongoose there was this Populate function, but I can't seem to find similar package / function in Meteor. So I was wondering if someone can help me by suggesting a resolution to this problem (if any). thanks
var newInvoice = {
customerid: $(e.target).find('[name=customer]').val(),
userid: Meteor.userId(),
//....more fields here
}
Meteor.call('saveInvoice', newInvoice, function(error, id){
if(error)
return alert(error.reason);
});