I've a Nodejs app and I use Passeport to let my users connect throught Facebook or Google via Oauth. I'm storing basic data after connection such as : uid, firstname etc.
How can I be assured that the uid I'm receiving will be always unique for a given user ? I mean, data is coming either from Facebook or Google, so why couldn't I face a nasty duplicate uid for different users someday ?
The id you are getting via OAuth is unique when you are using either Facebook or Google. But if you want to use both then I would suggest make your dbSchema (if you are using mongoose) like this -
var userSchema = new mongoose.Schema({
local: {
username: String,
password: String
},
facebook: {
id: String,
token: String,
email: String,
name: String
},
google: {
id: String,
token: String,
email: String,
name: String
}
});
The UID is unique only within the scope of that single provider’s list of users.
This article suggests you combine the provider’s name plus the UID to get a unique value.
Combination of provider’s name and uID will uniquely identify user inside our app.
I would use the provider’s domain name such as “google.com” or “com.google”.
Related
Well I have already done in mongoose schema by setting my property with unique:true like below
mongoose.Schema({ userName: { type: String, unique: true }})
this time i want to do it through my router api's
User.findOne({userName: req.body.userName})
it only works when i enter same string as save already in database.like
userName: ranatouseef
but if i enter userName: ranaTouseef it fails,
well regix don't work here like regix also have issue like that well if i search for userName: rana regix find it ranatouseef. well same goes for touseef to well my issu is find same string but without casesensitive,
how can i do that.
In this case, you should set the username in lower case.
new User({ username: req.body.userName.toLowerCase() });
User.save()
And when you want retrieve it, use the toLowerCase method on the argument.
User.findOne({ userName: req.body.userName.toLowerCase() })
You should always avoid to duplicate your data in your DB. Here, you are about to save a string in lowercase and in uppercase. I don't know your use case but it makes no sense to me.
Also, my solution solves the issue of "RanaTouseef" and "ranaTouseef" being 2 unique username
Here is how my application works. A user logs in for the first time using Google Sign in. We get the following data from their Google Account:
Given name
Family name
Email ID
We wish to use this information to call our API (POST request) to create a user profile.
The data we send is
{
firstName: firstName ,
lastName: lastName,
email: email
}
Here is where the issue comes from. The user profile has many fields and one of them is designation. When the user logs in for the first time, we don't know their designation.
We are using MongoDB for our database. So we use Mongoose to set up the connection. In Mongoose model, we have added some validation for our schema. Designation is a required field. It should be at least one character of length and maximum of 40 characters. If we set designation as null, the validation would fail.
Is there any way to allow null in a required field in Mongoose?
Rather than setting required to true or false, you can pass it a function:
const user = new Schema({
designation: {
type: String,
minLength: 1,
maxLength: 40,
required: function() {
// Rather than checking a stored variable, you could check
// static functions on the model, a custom value on the
// instance that isn't persisted, etc.
return this.hasLoggedInAtLeastOnce === true;
// If this function returns true, the field is required.
}
}
hasLoggedInAtLeastOnce: {
type: Boolean,
}
});
I'm new to mongodb and I'm working on my personal project which is a project management system for college projects. I have 3 users admin(the department), students, and advisor. The admin is the one who registers both the students and the advisors. All the users have some common fields like name, email, and password. And different fields of there own.
Their roles:-
- Admin -> adds the students and advisors
- Student -> choose projects and work on the projects in a team
- Advisor -> advice students based on their progress on their project
My problem is in designing the model should I use one userSchema and add all the users in one collection or create different collections for each user.
My second confusion is if I create different collections for each user how can I deal with authentication and authorization? I'm using node js for the backend.
Can I get some guidance and suggestion?
It's your choice.
Here, I would suggest to use a single Collection for all the 3 types of Users.
Since everyone would be having same functionalities like name, email, password etc. on registration, single Schema would work for sure.
So create on schema of suppose 'User' and then use one 'tag' selector to identify the admin, advisor and student.
I would do something like this:
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
},
name: {
type: String,
required: true,
},
tag : {
type: String
},
Addtasks: [
{
topic: String,
words: Number,
keywords: String,
website: String,
otherdetails: String,
exampleRadios: String,
deadline: Date,
Date: String,
fileName: String
},
],
});
module.exports = mongoose.model('User', userSchema);
With this create one page for authentication as auth.js separately and write the single authentication code there using passport module authentication. With that said now you can use one authentication validation for all 3 dashboards.
For handlebars as front-end use this to check the user if he/she has logged in or not.
{{if #user}}
<html>
<head>
.
.
.
...
following this you can achieve this.
Suppose the following User Schema in MongoDB (using Mongoose/Nodejs):
var UserSchema = new Schema({
email: {
type: String,
unique: true,
required: 'User email is required.'
},
password: {
type: String,
required: 'User password is required.'
},
token: {
type: String,
unique: true,
default: hat
},
created_at: {
type: Date,
default: Date.now
},
});
// mongoose-encrypt package
UserSchema.plugin(encrypt, {
secret: 'my secret',
encryptedFields: ['email', 'password', 'token', 'created_at']
});
Now assume I want to return the user object from an API endpoint. In fact, suppose I want to return user objects from multiple API endpoints. Possibly as a standalone object, possibly as a related model.
Obviously, I don't want password to be present in the returned structure - and in many cases I wouldn't want token to be returned either. I could do this manually on every endpoint, but I'd prefer a no-thought solution - being able to simply retrieve the user, end of story, and not worry about unsetting certain values after the fact.
I mainly come from the world of Laravel, where things like API Resources (https://laravel.com/docs/5.6/eloquent-resources) exist. I already tried implementing the mongoose-hidden package (https://www.npmjs.com/package/mongoose-hidden) to hide the password and token, but unfortunately it seems as though that breaks the encryption package I'm using.
I'm new to Nodejs and MongoDB in general - is there a good way to implement this?
How to protect the password field in Mongoose/MongoDB so it won't return in a query when I populate collections?
You can use this: Users.find().select("-password"),
but this is done whenever you send the queried item to the user (res.json()...) so you can do your manipultions with this field included and then remove it from the user before you send it back (this is using the promise approach, the best practice).
And if you want your changes to be used as default you can add "select: false" into the schema object's password field.
Hope this helps :)
I have an idea for how to store the relationships. Each user has a friends Array filled with IDs. However, how should I initiate a friend request in my Express.js app in MongoDB?
I'm thinking about creating a "notifications" collection with:
_id, userId, type, friendId, read
So when the requested friend logs in, they can see all of their own notifications to deal with...
Or is that ridiculous?
For such notifications what I did, is as follows:
var notificationSchema = mongoose.Schema({
to:{
type: mongoose.Schema.Types.ObjectId, ref: 'User'
},
from:{
type: mongoose.Schema.Types.ObjectId, ref: 'User'
},
for: {
type: String
},
status: {
type: String,
default: "Not Seen"
},
description:{
type: String
}
},{ timestamps: { createdAt: 'created_at' }
});
where I have saved _id of to and from (users) where I used for field for notification type like following, liked etc and used description field as optional.
Sounds reasonable enough. But my approach will be a little different. I would store the notifications in the user db itself. Something like
username
dob
...
notifications[[type: 'friend', read: 0, request_from: '22sd300sdf45003425asz'], ...]
...
This way, you don't have to make a db call on every page load. As soon as you initialize a session (I use passport), it will be there, ready already for templates. After a valid action from the user, I can delete it or whatever.
But again, its dependent on the need. Do what suits you best!
If you store it in the user passport session (solution mentioned earlier) you will not be able to receive anymore notifications since it is static information in the header and not connected directly to the document store.
The best way to do it would to have let it have it's own store.
{Id:ObjectId, for_user:String, sent from:String, status:Boolean}
Perhaps you can initially set the status to null then set it to true or false when a user accepts or denies it. After create the user to user friend relationship. That's more or less the way I would go about it.