Refresh Tokens in MongoDB - javascript

What is the best solution to remove a refresh token from MongoDB automatically.
On login the user is given a temporary auth token which lasts 30 seconds. They are also given a permanent token, stored in MongoDB, which currently lasts until they log out.
I want to remove the permanent one at the end of every day, but I'm not sure how to do that without having a cron job running (to monitor what time it is). This seems a bit complex for such a small task. Is there a way mongo would know what the time is and then remove the refresh token?
This is how the token collection looks:
Thank you

To auto-delete the MongoDB documents after some time, you should use the TTL(time to live) collection feature documented here.
Essentially, you need to create an index on the collection that stores the token documents. For your use case, you can do something like this:
// This would delete the tokens document after 3600seconds after creation
// You can tweak the time as you wish.
db.tokens.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 });

NodeJS, mongodb.
Just simply create a model for each token.
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const tokenSchema = new Schema({
_userId: {
type: Schema.Types.ObjectId,
required: true,
ref: 'user'
},
token: {
type: String,
required: true
},
expireAt: {
type: Date,
default: Date.now,
index: { expires: 60*60*24 }
}
})
module.exports = mongoose.model('tokens', tokenSchema)

Related

Duplicate key error while using _id from another collection

I have 2 collections so far.
The first is a 'Users' collection (That works well), and the other is a 'Rooms' collection (both created for a chat application).
I want every room to have a "users" array that will contain the user._id of every user that is in that room,
meaning I should be able to put the same user._id (from the user collection) in every one of the rooms right?
After creating a room successfully with 2 user._ids in the "users" array,
I tried making another one using one of the user._ids I used in the first room.
Then I got this error:
MongoError: E11000 duplicate key error collection: ratchat.rooms index: users_1 dup key:
{ users: "5fe08d452f34530e641d8f8c" }
After checking with a debugger I've found that the error occurs only when I use a user._id that is already used in another room's "users" array.
The only thing I could think of that could cause this problem is the Room schema,
maybe there's something I missed while reading the docs...
At first my Room schema looked like this:
const roomSchema = new mongoose.Schema({
users: [String],
hasLeft: [String],
isGroup: { type: Boolean, default: false },
},
});
const Room = mongoose.model("Room", roomSchema);
Then I thought maybe mongoDB needs to know that the ObjectIds that are in the users array are just references to another collection:
const roomSchema = new mongoose.Schema({
users: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
hasLeft: [String],
isGroup: { type: Boolean, default: false },
},
});
const Room = mongoose.model("Room", roomSchema);
No luck so far...
{autoIndex: false}
After research, I have found the reason for this error:
mongoose automatically creates indexes,
this is not only a duplicate error issue, but can cause a significant performance impact later in production.
According to mongoose docs, you can easily disable this behavior by setting the autoIndex option of your schema to false, or globally on the connection by setting the option autoIndex to false.
mongoose.connect('mongodb://user:pass#localhost:port/database', { autoIndex: false });
// or
mongoose.createConnection('mongodb://user:pass#localhost:port/database', { autoIndex: false });
// or
animalSchema.set('autoIndex', false);
// or
new Schema({..}, { autoIndex: false });
Don't forget to drop the entire collection before trying again
Because the collection is already indexed, emptying it completely won't work.
You have to drop the entire collection.

Mongoose how to update array from child to parent

I have following schema for Audio.
const AudioSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
uploaderId: {
type: String,
required: true
}
});
Instead of referencing the User, I just store the User's _id as uploaderId.
In my User schema I also have audioFiles: [Audio] array for all audio files that user has uploaded.
const UserSchema = new mongoose.Schema({
...,
audioFiles: [Audio]
});
When I try to update my AudioSchema from my REST Api, I can change all the properties and that works, but after saving this Audio model those changes doesn't affect the User model.
Now I created a new branch and try to change uploaderId to UserSchema. But I wonder is there a solution for this without referencing the UserSchema
I managed to do this with help of MongooseArray.prototype.pull method.
Steps for solving this problem is really easy.
First I get the User that is associated with AudioModel.uploaderId, then I used the user.audioFiles.pull() method. Correct code is below.
let user = await UserService.getUser(userId);
await user.audioFiles.pull({
_id: audioId //audioId is the id which i'm trying to remove from array
});
await user.save();
I also added try-catch block to handle errors.
Anyone having this kind of issue can use the link below to get more information about MongooseArray.prototype.pull method.
Also you can check the other answers in this post.

Creating First Admin in Node.js App

I've created my first Node.js API and now I am at the step where I need to have initial admin when I push the code live.
My app works the way that there is one admin which can send invitations to users and users can register only with that invitation. My model looks like this:
var userSchema = new Schema({
"email" : { type: String, unique: true },
"pass" : { type: String, required: true },
"type" : { type: String, required: true },
"invitation" : String
});
Field type can have a value admin or regular. For each user creation, email/invitation check is done so users can't be created without invitation.
One of my thought was to have a check each time when database opens:
var mongoose = require("mongoose");
mongoose.connection.on('open', function(){
// get all users
User.find({}, function(err, data){
// if there are no users at all, create first admin
});
});
Credentials for first admin I would keep in env variables and fetch them like this:
var firstAdminEmail = process.env.FIRST_ADMIN_EMAIL;
var firstAdminPass = process.env.FIRST_ADMIN_PASS;
To prevent invitation check in user creation, I would create a separate function which would ignore invitation in user creation.
What do you think about this? What would be the best practice for creating that first admin?

How do I get keystoneJS to run a function once a specific model has been updated?

I want to run a function once I have a new or updated item in a specific KeystoneJS model. How do I do that? Would I add an event... is there an event already? Do I add it in the model or elsewhere?
You can use mongoose middleware as with any non-keystone project. A keystone lists schema can be accessed with .schema, e.g.
var keystone = require('keystone');
var Types = keystone.Field.Types;
var User = new keystone.List('User');
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
});
//do stuff BEFORE the user document is fully saved to the DB
User.schema.pre('save', function(next){
console.log('SAVING USER:', this);
next();
});
//do stuff AFTER the user document has been saved to the DB
User.schema.post('save', function(user){
console.log('USER WAS SAVED:', user);
});
User.defaultColumns = 'name, email';
User.register();
Take a look at mongoose middleware, since some restrictions apply, for instance when doing mass updates the middleware will not run, this is by design in mongoose and has nothing to do with keystone.
KEystoneJS uses mongoose.
When you add new model, the CRUD(change,Replace,Update, Delete) happens naturally in Admin side(http:///keystone
However for non-admin side You need to make Routes, and in routes views you can use mongoose API to do so.
any change in in keystoneJS, can be made working just by restarting the server (nam start)

Mongoose/Node server restart and duplicates

Ok so after a ton of trial and error, I've determined that when I drop a collection and then recreate it through my app, unique doesn't work until I restart my local node server. Here's my Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Services = new Schema ({
type : {type : String},
subscriptionInfo : Schema.Types.Mixed,
data : Schema.Types.Mixed
},{_id:false});
var Hashtags = new Schema ({
name: {type : String},
services : [Services]
},{_id:false});
var SubscriptionSchema = new Schema ({
eventId : {type: String, index: { unique: true, dropDups: true }},
hashtags : [Hashtags]
});
module.exports = mongoose.model('Subscription', SubscriptionSchema);
And Here's my route...
router.route('/')
.post(function(req, res) {
var subscription = new subscribeModel();
subscription.eventId = eventId;
subscription.save(function(err, subscription) {
if (err)
res.send(err);
else
res.json({
message: subscription
});
});
})
If I drop the collection, then hit the /subscribe endpoint seen above, it will create the entry but will not honor the duplicate. It's not until I then restart the server that it starts to honor it. Any ideas why this is? Thanks!
What mongoose does when your application starts and it itself initializes is scan your schema definitions for the registered models and calls the .ensureIndexes() method for the supplied arguments. This is the "by design" behavior and is also covered with this statement:
When your application starts up, Mongoose automatically calls ensureIndex for each defined index in your schema. While nice for development, it is recommended this behavior be disabled in production since index creation can cause a significant performance impact. Disable the behavior by setting the autoIndex option of your schema to false.
So your general options here are:
Don't "drop" the collection and call .remove() which leaves the indexes intact.
Manually call .ensureIndexes() when you issue a drop on a collection in order to rebuild them.
The warning in the document is generally that creating indexes for large collections can take some time and take up server resources. If the index exists this is more or less a "no-op" to MongoDB, but beware of small changes to the index definition which would result in creating "additional" indexes.
As such, it is generally best to have a deployment plan for production systems where you determine what needs to be done.
This post seems to argue that indexes are not re-built when you restart: Are MongoDB indexes persistent across restarts?

Categories