Passport.js: accessing user when using backbone - javascript

I'm using passport.js in a node.js app to get OAuth working (awesome!), but now, I'm having problem with one thing.
I'm using backbone.js on client side, to create the views (I'm trying to create a SinglePage app...), and the only way I'm thinking about getting user id or something like that in backbone is creating first an input-hidden in jade templates and then passing it to the constructor in my backbone views.... or just assign the value to a javascript variable and passing it to the router that manages all the views, and pass it to every view that need it
I think it's not a good idea, and there might be better options!
How would you do it?
Thanks!

I haven't done anything in Backbone yet, but there are solutions here, here and here already. As long as you get the session id somewhere, you can retrieve the session with the cookie-package (cookie has been outsourced from connect-utils):
if(handshakeData.headers.cookie) {
c = cookie.parse(handshakeData.headers.cookie);
sid = connect.utils.parseSignedCookie(c['connect.sid'], 'keyboard cat');
redis.get('sess:'+sid, function(error, result) {
session = JSON.parse(result);
handshakeData.uid = session.passport.user;
callback(null, true);
});

Related

Caching select query data on server side

I am writing an express app, where I'm pushing data from my views to a database. But most of the data is mapped to some other data in database tables.
For example, is a choose student name drop down- once you choose the student by his name , a drop down below - will show all roles that he is allowed for.
So I'm following this pattern of
app.post('\action1', function(req,res){
function querySomething(){
var defered = Q.defer();
connection.query(some_select_query,defered.makeNodeResolver());
return defered.promise;
}
function querySomethingElse(){
var defered = Q.defer();
connection.query(some_other_select_query,defered.makeNodeResolver());
return defered.promise;
}
Q.all([querySomething(), querySomethingElse()]).then((results,err) => {
connection.release()
if(results){
res.render('some_view.ejs', {
result1:results[0][0],
result2:results[1][0]
});
}
else{
res.render('error.ejs',{});
}
})
})
Now the problem is that I have to follow this pattern of selecting something from multiple tables, pass all these function to a promise- and when the results is passed back, goto my view with all those result objects - so that I can use them in my view - as a means of doing drop downs dependent on one another.
Sometimes I have to re-write this multiple times.
Doing a select query like this would be performance intensive especially if all views are using the result of the same query.
Is there any way I can build a cached data store on my express server side code and query that instead of the actual database??
If there is an insert or an update - i will refresh this store and just do a new select * that one time.
What libraries are there on top of express which will help me do this??
Does mysql-cache does the same thing?? I'm also using connection pooling with createPool.
How do I achieve this - or do I just restore to using big mvc's like sails to rewrite my app?
You can try apiCache npm module.
"Sometimes I have to re-write this multiple times."
Based on the business need, you may want to handle each use case separately and this scenario doesn't deal with caching.
Doing a select query like this would be performance intensive especially if all views are using the result of the same query.
This is a classic example for the need of server-side caching.

JS: Node.js and Socket.io - globals and architecture

Dear all,
Im working with JS for some weeks and now I need a bit of clarification. I have read a lot of sources and a lot of Q&A also in here and this is what I learned so far.
Everything below is in connection with Node.js and Socket.io
Use of globals in Node.js "can" be done, but is not best practice, terms: DONT DO IT!
With Sockets, everything is treated per socket call, meaning there is hardly a memory of previous call. Call gets in, and gets served, so no "kept" variables.
Ok I build up some chat example, multiple users - all get served with broadcast but no private messages for example.
Fairly simple and fairly ok. But now I am stuck in my mind and cant wrap my head around.
Lets say:
I need to act on the request
Like a request: "To all users whose name is BRIAN"
In my head I imagined:
1.
Custom object USER - defined globally on Node.js
function User(socket) {
this.Name;
this.socket = socket; }
2.
Than hold an ARRAY of these globally
users = [];
and on newConnection, create a new User, pass on its socket and store in the array for further action with
users.push(new User(socket));
3.
And on a Socket.io request that wants to contact all BRIANs do something like
for (var i = 0; i < users.length; i++) {
if(user[i].Name == "BRIAN") {
// Emit to user[i].socket
}}
But after trying and erroring, debugging, googling and reading apparently this is NOT how something like this should be done and somehow I cant find the right way to do it, or at least see / understand it. can you please help me, point me into a good direction or propose a best practice here? That would be awesome :-)
Note:
I dont want to store the data in a DB (that is next step) I want to work on the fly.
Thank you very much for your inputs
Oliver
first of all, please don't put users in a global variable, better put it in a module and require it elsewhere whenever needed. you can do it like this:
users.js
var users = {
_list : {}
};
users.create = function(data){
this._list[data.id] = data;
}
users.get = function(user_id){
return this._list[user_id];
};
users.getAll = function(){
return this._list;
};
module.exports = users;
and somewhere where in your implementation
var users = require('users');
For your problem where you want to send to all users with name "BRIAN",
i can see that you can do this good in 2 ways.
First.
When user is connected to socketio server, let the user join a socketio room using his/her name.
so it will look like this:
var custom_namespace = io.of('/custom_namespace');
custom_namespace.on('connection', function(client_socket){
//assuming here is where you send data from frontend to server
client_socket.on('user_data', function(data){
//assuming you have sent a valid object with a parameter "name", let the client socket join the room
if(data != undefined){
client_socket.join(data.name); //here is the trick
}
});
});
now, if you want to send to all people with name "BRIAN", you can achieve it by doing this
io.of('/custom_namespace').broadcast.to('BRIAN').emit('some_event',some_data);
Second.
By saving the data on the module users and filter it using lodash library
sample code
var _lodash = require('lodash');
var Users = require('users');
var all_users = Users.getAll();
var socket_ids = [];
var users_with_name_brian = _lodash.filter(all_users, { name : "BRIAN" });
users_with_name_brian.forEach(function(user){
socket_ids.push(user.name);
});
now instead of emitting it one by one per iteration, you can do it like this in socketio
io.of('/custom_namespace').broadcast.to(socket_ids).emit('some_event',some_data);
Here is the link for lodash documentation
I hope this helps.

How do i save model data when creating a Backbone driven theme in Wordpress?

Ok so im messing around with Backbone for the first time. I think I've pretty much covered all the basics of frontend logic, but i have never really been any good at backend logic and coding.
I'm working with wordpress and creating a theme using backbone. My understanding is as long as i set up a template page that has the correct containers that my backbone code will render views in, the fact that it's a wordpress theme instead of it's own app shouldn't really change anything on the frontend side.
I'm at the stage where i want to save a model so that i can fetch it in my routes to link to my view to render.
I'm unsure about the whole process of saving data. I know i need to give the model attribute 'urlRoot' a string but i don't know what that string should be, and what happens after that.
Can someone explain the whole process, especially in terms of how to do it with Wordpress. (i did stumble upon the WP REST API plugin that i think helps, although i don't exactly know how.)
EDIT
OK so in the end i presume my problem was something to do with authentication when trying to access the database as the textResponse was just returning the entire HTML for the current page i was on, probably due to the fact it wasn't getting through to the database and being redirected back to the page.
After googling around for a while i came across this. Rather than reinventing the wheel I installed this plugin and followed the setup instructions and low and behold it worked pretty much out of the box. If your trying to build a Backbone theme i suggest using the WP-API Client JS plugin with the WP REST API plugin. Seems to cover everything.
How to expose a WordPress blog's content through an API?
WP REST API seems like a good way to start. There are a lot of options and it exposes everything you need.
Note that it is named WordPress REST API (Version 2) in the wordpress.org plugin directory.
You can test that the plugin works by navigating to:
http://www.example.com/wp-json/wp/v2/
It should output all the information on the blog as a big JSON dump.
You can also test that it works for other endpoints, like post:
http://www.example.com/wp-json/wp/v2/posts
There's a Backbone plugin for the WP REST API that works out of the box.
How to communicate with the API?
This is a simple example using Backbone without any plugin. If you want to know how to use the plugin, see the documentation for it.
Since it offers a lot of arguments that can be passed in the URL, I made a small collection and an example of how it could be used.
var API_ROOT = '/wp-json/wp/v2/',
DEFAULT_API_ARGS = ['context' /* etc. */ ];
var WordPressCollection = Backbone.Collection.extend({
constructor: function(models, options) {
options = options || {};
this.apiArgs = _.union(DEFAULT_API_ARGS, this.apiArgs, options.apiArgs);
this.args = _.extend({}, this.args, this.getApiArgs(options));
WordPressCollection.__super__.constructor.apply(this, arguments);
},
getApiArgs: function(obj) {
return _.pick(obj, this.apiArgs);
},
fetch: function(options) {
options = options || {};
options.data = _.extend({}, this.args, this.getApiArgs(options), options.data);
return WordPressCollection.__super__.fetch.call(this, options);
},
});
And to use it:
var CommentCollection = WordPressCollection.extend({
url: API_ROOT + 'comments',
// all the arguments to look for in the passed options
apiArgs: ['page', 'per_page', 'post' /* etc. */ ],
});
var myPostComments = new CommentCollection(null, {
post: 23 // id
});
console.log(myPostComments.url());
myPostComments.fetch({ page: 2 });
The fetch should make a GET request to:
/wp-json/wp/v2/comments?post=23&page=2
And from that point, the WP REST API plugin takes control. It returns a new JSON encoded array of comment objects in the body of the response.
It should looks something like this:
Backbone automatically parses the JSON received, so you don't need to worry about that and you just have to go on and use it:
myPostComments.each(function(comment) {
console.log(comment.get('author_name'));
});
Then, saving a new comment is a matter of calling:
// check the doc for the comment object details
myPostComments.create({
post: 23,
content: "my new comment",
/* etc. */
});
And this would make a POST request to /wp-json/wp/v2/comments.

Mongoose how to replace a Model's save() function

I have set up a KeystoneJS project which allows you to get an out-of the box Admin UI for your models.
KeystoneJS works with only one master database, where you define models, and then each Model get its own collection in that master database.
The thing is, I have a separate user database and a separate content database. I am looking to "hijack" the Keystone Models, so that I can plug in the models from my other databases (currently using the mongoose-glue project).
I am 50% there. I got it to read data by replacing the Keystone model's .find() and .findOne() functions like this
var KeystoneUser = new keystone.List('User');
KeystoneUser.add({ /* clone all fields in external model */ });
KeystoneUser.register();
var external = require("mongoose-glue");
var ExternalUser = external.model("ExternalUser")
KeystoneUser.model.find = ExternalUser.find.bind(ExternalUser)
KeystoneUser.model.findOne = ExternalUser.findOne.bind(ExternalUser)
Using the above the Keystone Admin UI works super good for listing and browsing the data in the external database.
But the problem is when it comes to saving.
How can I replace the save function in a similar manner? Also, for some reason when doing the above, all pre/post hooks stop working on both the keystone and the external model..
Solved it with:
KeystoneUser.model.collection = ExternalUser.collection;
That got the hooks to work, so then I just wrote my own save/update hook
KeystoneUser.schema.pre("save", function(next) {
// Trick to remove everything we don't need
var data = JSON.parse(JSON.stringify(this));
if (typeof this.__v === "undefined") {
new ExternalUser(data).save();
} else {
delete data._id;
ExternalUser.findByIdAndUpdate(this._id, data).exec();
}
next();
});

Authentication with devise rails using backbone

I am new to backbone.js. I am developing a rails application using "backbone-on-rails" gem. I have included 3 models and rendering views uisng backbone. It worked fine. Now i want to add authentication to my app using devise, after the user has signed in only my app needs to be rendered otherwise i need to redirect to login page.
I have added devise gem for it.
Can someone please help me on how can i check whether user has signed in or not, if user hasn't logged in need to redirect to devise sign_in page using backbone?
Thanks in Advance
Backbone's a frontend-only framework, so it doesn't have a concept of authentication. All the source code is sent to the web browser, and all the network connections are plain to see, so a malicious user can trick your app into thinking it's logged in, even if it isn't. So you'll still need to check access permissions on the server.
What you can do, though, is have your Backbone app detect whether it thinks it's logged in, and change its display based on that. For instance, you could use Devise's user_signed_in? helper to add a data attribute on your body tag, and hook into that. Something like this in your app/views/layouts/application.html.erb:
<body data-user-signed-in="<%= user_signed_in? ? "true" : "false" %>">
And then, maybe your Backbone router is going to look something like this:
myApp.Routers.Router = Backbone.Router.extend({
routes: {"": "showFrontPage"},
isSignedIn: function() {
return $('body').data('user-signed-in') === "true";
},
showFrontPage: function() {
var view;
if (this.isSignedIn()) {
view = new myApp.Views.MainAppView();
} else {
view = new myApp.Views.SignInView();
}
view.render();
}
});
Alternatively, you could look directly for a session cookie. That seems a bit more brittle to me, though; if the name of your application changes, or Rails changes how it names its cookies, your app's going to break. But in that case, your isSignedIn() function is going to look more like this:
isSignedIn: function() {
return document.cookie.indexOf("_Railsappname_session") > -1;
}
If you want to check your user at various points of your app, you could easily write a controller method that returns the result of user_signed_in? as a JSON object. But it's better not to rely on this; rather than calling /user/is_signed_in and then /posts/create, far better to do one call to /posts/create and have that return a 401 Unauthorized if the user's not logged in.
As for logging in itself, you can adapt Devise to work via JS so you can login via AJAX, but it's not as straightforward as you might hope. There's a tutorial here.
I needed to implement a backbone login in rails with devise. Note that for my purposes I did not need user registration as well, since I wanted to have just one admin user, created manually in the terminal by me.
Basically as long as you make an AJAX post request to the right devise route, devise will handle the login for you (assuming of course you went through the devise setup process correctly).
In Backbone you can make this post request using a new model save.
This tutorial helped me set up my Backbone model and view (FYI: the tutorial also goes over what you need to do in order to add registration functionality).
The tutorial had some more advance backbone setup (for example it uses backbone.marionette and backbone.modelbinder) which although very useful, I did not want to get into. Below is my simplified version of the tutorial to the bare core of what you need.
Create a model with the urlRoot that matches your devise login route. For most people that go with the standard User model, the urlRoot route below should work. Note my code is written in coffeescript
class MyCoolBackboneApp.Models.UserSession extends Backbone.Model
urlRoot: '/users/sign_in.json'
defaults:
email: ""
password: ""
toJSON: ->
{ user: _.clone(#attributes) }
Note that devise expects the params to be wrapped inside 'user' which is why we had to overwrite the toJSON method
Then in your view, all you need to do is save the model together with the login credentials. Of course every person might have a different success and failure callback, but here is a very basic implementation:
events:
'submit form': 'login'
initialize: =>
#model = new MyCoolBackboneApp.Models.UserSession()
render: =>
$(#el).html( #template() )
#
credentials: ->
{
email: #$('#email').val(),
password: #$('#password').val(),
remember_me: 1
}
login: (event)->
event.preventDefault()
#model.save(#credentials(),
success: (userSession, response) =>
window.location.href = "/"
error: (userSession, response) =>
message = $.parseJSON(response.responseText).error
alert(message)
)
You should also read this tutorial about how to set up devise ajax authentication.
After you complete the above tutorial, you should be able to save your UserSession model with the right credentials (as I do in the view) and login successfully (assuming you have a saved existing user in your Database). You'll know you've logged in successfully when you get redirected to your success callback.
Then in the controllers in the rest of your app, you should be able to use the devise helpers: user_signed_in? or current_user etc etc.
(If you are logged in but get an undefined method error for these helpers, try to add: include Devise::Controllers::Helpers to your controllers).
Finally Alex P's response can then walk you through how to use the user_signed_in? boolean in your Backbone views.

Categories

Resources