Meteor: onConnection, check if user is logged in - javascript

I'm using the onConnection hook and some template helpers to do some stuff with statistics. But now, I want't to exclude these operations when I'm a registered user.
The Problem, I can't use Meteor.user() in the onConnection hook, so how can i check if a user is logged in ?
Concerning code, there is not much to show
Meteor.onConnection(function(conn) {
if(Meteor.user()) {
console.log("you are logged in")
} else {
console.log("u are not logged in")
}
});
It's not the true example but it shows simple what i want to do
The Error
err [Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.]
I understand that i can just use Meteor.user() in methods, but how can i find out in the onConnection if a user is logged in ?

For statistics purposes I'd recommend to use publications. They have more sophisticated api which allows you to have more control over your connection.
Meteor.publish('users.trackPresence', function() {
// Both this.userId && this.connection are available to be called from here
this.onStop(function(){
// user went offline
});
this.ready();
});
and on the client you can check if user is present and not even subscribe if this is the case:
Tracker.autorun(function(){
if (!Meteor.userId())
Meteor.subscribe('users.trackPresence');
});
Meteor automatically handles unsubscribe/resubscribe when you subscribe from within a Tracker.autorun
read more about pubsub api here
https://docs.meteor.com/api/pubsub.html

Obviously as you stated, the Meteor docs do not provide any insight for how to achieve this. I spent a decent amount of time going thru the accounts-base source and don't see any way to do what you are asking natively.
With that said, if you could update the Users collection each time they login and save their current IP address, then you could use this as a way to see if the current connection is logged in. Here is an example.
Meteor.onConnection((connection) => {
var user = Meteor.users.findOne({
'user.profile.currentIp': connection.clientAddress
});
if (user) {
console.log("you are logged in")
} else {
console.log("u are not logged in")
}
});
Be sure to add login and logout hooks to set and remove the user's current IP. I have not tested this approach, but in theory it should work.

Related

Firebase User Already Logged In

I have a firebase app that I can log into from various devices, but I'd like to disconnect the other connections if I make a new one using the same account.
I saw this bit of code but I think this might be for the old version:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
This looks like the right idea - if this gets called I could show a graphic saying, "Oops looks like you signed in on another device." then fire a disconnect while allowing the other connection to proceed?
This isn't something you'll be able to handle with auth alone, as the tokens are generated and stored independently and there's no concept of "device sessions" that can be queried against. However, you could do something like this:
var deviceId = generateARandomDeviceIDAndStoreItInLocalStorage();
firebase.auth().signInWithPopup(/* ... */).then(function(user) {
var userRef = firebase.database().ref('users').child(user.uid);
return userRef.update({
deviceId: deviceId
});
});
firebase.auth().onAuthStateChanged(function(user) {
var userRef = firebase.database().ref('users').child(user.uid);
userRef.child('deviceId').on('value', function(snap) {
if (snap.val() !== deviceId) {
// another device has signed in, let's sign out
firebase.auth().signOut();
}
});
});
IMPORTANT CAVEAT: This is not a secure, enforceable way to guarantee only one device is logged in at once. Rather it is a client-driven way to generally achieve the goal of only one device being signed in.

Redirect a user if they do not have a set password before publishing

I have an interface in my website where a user can create an "unclaimed" order for another user and send them an invitation. The invitation link will be formatted as /enrollment/orders/:_id, where _id is the id of the order.
One catch is that this can be sent multiple times. On the first time, the user that is invited might not have a password set.
Meteor.publish('enrolled_order', function (token) {
// if user has their password reset, token will also be set for user's account
return Orders.find({
'enrollment.token': token
});
});
Here's the catch: During this publication, I want to check certain aspects of the user record and take different actions instead of publishing it. For security, I believe this will need to be done on the server to work appropriately.
if there is no this.userId, I want to send them to login.
if the user does not have a password set, I want to redirect them to the reset password page.
Is this possible via a meteor publication?
I think you would need to do this in the router. If you're using Iron Router, you could use onBeforeAction to check whether the user is logged in:
Router.route('/enrollment/orders/:_id', {
subscriptions: function() {
return Meteor.subscribe('enrolled_order', this.params._id);
},
onBeforeAction: function() {
if( ! Meteor.userId() ) {
Router.go( 'loginTemplateName' );
}
}
});
You aren't going to want to return the hashed password directly to the client, so maybe setting a variable to say whether the password exists or not might be the best approach?
I can't remember if onBeforeAction happens before waitOn, but you might be able to get away with waitOn instead of subscriptions.

How can I display a list of all LOGGED IN users with Meteor.js

I have been trying for days to get a list of logged in users in a Meteor chat app.
I tried many different things. I managed to add a login flag on the user profile object.
Server side:
Accounts.onCreateUser(function(options, user) {
if(!options.profile){
options.profile = {}
}
options.profile.login = false;
if (options.profile)
user.profile = options.profile;
return user;
});
In the browser console I get this:
Meteor.user().profile
Object {login: false}
So that seems to work.
Now I want to list if users are logged in:
Client side
Deps.autorun(function(){
if(Meteor.userId()){
Meteor.user().profile.login=true;
}
});
After checking the login remains false when I log in.
This template html gives me a list of all usernames but not the login flag
{{#each allUsers}}
<p>{{username}}</p><p>{{profile.login}}</p>
{{/each}
So my problems are : profile.login remains false and I cannot display profile.login but the usernames are displayed.
Thank you in advance. Greetings Joris
To change the users profile.login property you need to do Meteor.users.update(..) or call a server method that does that. Just changing the user object's property will not work.
Generally I would recommend to not persist the users state into the mondodb database but hold it in a Collection in memory.
The easiest might be to just use one of these packages:
https://github.com/dburles/meteor-presence/
https://github.com/mizzao/meteor-user-status
or study their source code to see how to propagate the user status.

Meteor.user() alternatives

I am writing an application which needs to display some user information.
Because Meteor.user() is not immediately available I wrapped every user information with an handlerbar helper
Handlebars.registerHelper('isLoggingIn', function() {
return Meteor.loggingIn();
})
This worked for me until I needed to create an admin page and custom content for every user/user role.
Waiting for Meteor.user() to be available or showing general information first while waiting for the roles to load are options I would like to avoid.
I then tried an alternative way and published the currentUser with a new Collection.
Meteor.publish('currentUser', function() {
var sub = this;
var handle = Meteor.users.find({_id: this.userId}).observe({
added: function (user) {
sub.added('currentUser', user._id, user);
}
});
sub.ready();
sub.onStop(function() { handle.stop(); });
});
and
CurrentUser = new Meteor.Collection('currentUser');
In this way I can access the logged in user with CurrentUser.findOne(), and it's available at the same time as the other collections.
What I fear is that this alternative is not as secure and problem free as the common Meteor.user(), and I was wondering if my method is correct and if there are better ways to obtain the same result (user detail information immediately available) without reinventing the wheel.
Just a note you can use {{loggingIn}}, {{#if loggingIn}}.. without writing your own helper.
The option to publish the user who is logged in with a custom publish function adds an unnecessary complexity.
When it comes to security you have to assume if its from the client side, in any scenario it is untrustworthy. This means you publish relevant data for the role, etc only when they are logged in to that user.
On the server the data is immediately available as soon as the user logs in, all you have to do is publish only the data for that users role. On the client it may take some time to adjust to this, which is why you can use placeholder until the subscriptions are complete.
What might be a better option would be to use either a helper that checks for when subscriptions are completed and displays a 'loading message'. Or use a router such as iron-router (github.com/EventedMind/iron-router) that can let you wait for a subcription to complete for a particular page.
This way you can use Meteor.user(), {{#currentUser}} and roles in way you intend.
One thing to keep in mind, is if you want to check if the user is logged in, not to use:
if(Meteor.user())
but instead
if(Meteor.user() && Meteor.user().profile && Meteor.user().profile.name)
(You will have to insert a name property in your profile, though). While logging in the user gets more and more data. I've noticed if you wait for the profile field, then the user is 'ready'. It seems initially the profile field is empty (still loggin in), but it would return true if you used if(Meteor.user())

Picking up meteor.js user logout

Is there any way to pick up when a user logs out of the website? I need to do some clean up when they do so. Using the built-in meteor.js user accounts.
I'll be doing some validation using it, so I need a solution that cannot be trigger on behalf of other users on the client side - preferably something completely server side.
You may use Deps.autorun to setup a custom handler observing Meteor.userId() reactive variable changes.
Meteor.userId() (and Meteor.user()) are reactive variables returning respectively the currently logged in userId (null if none) and the corresponding user document (record) in the Meteor.users collection.
As a consequence one can track signing in/out of a Meteor application by reacting to the modification of those reactive data sources.
client/main.js :
var lastUser=null;
Meteor.startup(function(){
Deps.autorun(function(){
var userId=Meteor.userId();
if(userId){
console.log(userId+" connected");
// do something with Meteor.user()
}
else if(lastUser){
console.log(lastUser._id+" disconnected");
// can't use Meteor.user() anymore
// do something with lastUser (read-only !)
Meteor.call("userDisconnected",lastUser._id);
}
lastUser=Meteor.user();
});
});
In this code sample, I'm setting up a source file local variable (lastUser) to keep track of the last user that was logged in the application.
Then in Meteor.startup, I use Deps.autorun to setup a reactive context (code that will get re-executed whenever one of the reactive data sources accessed is modified).
This reactive context tracks Meteor.userId() variation and reacts accordingly.
In the deconnection code, you can't use Meteor.user() but if you want to access the last user document you can use the lastUser variable.
You can call a server method with the lastUser._id as argument if you want to modify the document after logging out.
server/server.js
Meteor.methods({
userDisconnected:function(userId){
check(userId,String);
var user=Meteor.users.findOne(userId);
// do something with user (read-write)
}
});
Be aware though that malicious clients can call this server method with anyone userId, so you shouldn't do anything critical unless you setup some verification code.
Use the user-status package that I've created: https://github.com/mizzao/meteor-user-status. This is completely server-side.
See the docs for usage, but you can attach an event handler to a session logout:
UserStatus.events.on "connectionLogout", (fields) ->
console.log(fields.userId + " with connection " + fields.connectionId + " logged out")
Note that a user can be logged in from different places at once with multiple sessions. This smart package detects all of them as well as whether the user is online at all. For more information or to implement your own method, check out the code.
Currently the package doesn't distinguish between browser window closes and logouts, and treats them as the same.
We had a similar, though not exact requirement. We wanted to do a bit of clean up on the client when they signed out. We did it by hijacking Meteor.logout:
if (Meteor.isClient) {
var _logout = Meteor.logout;
Meteor.logout = function customLogout() {
// Do your thing here
_logout.apply(Meteor, arguments);
}
}
The answer provided by #saimeunt looks about right, but it is a bit fluffy for what I needed. Instead I went with a very simple approach like this:
if (Meteor.isClient) {
Deps.autorun(function () {
if(!Meteor.userId())
{
Session.set('store', null);
}
});
}
This is however triggered during a page load if the user has not yet logged in, which might be undesirable. So you could go with something like this instead:
if (Meteor.isClient) {
var userWasLoggedIn = false;
Deps.autorun(function (c) {
if(!Meteor.userId())
{
if(userWasLoggedIn)
{
console.log('Clean up');
Session.set('store', null);
}
}
else
{
userWasLoggedIn = true;
}
});
}
None of the solutions worked for me, since they all suffered from the problem of not being able to distinguish between manual logout by the user vs. browser page reload/close.
I'm now going with a hack, but at least it works (as long as you don't provide any other means of logging out than the default accounts-ui buttons):
Template._loginButtons.events({
'click #login-buttons-logout': function(ev) {
console.log("manual log out");
// do stuff
}
});
You can use the following Meteor.logout - http://docs.meteor.com/#meteor_logout

Categories

Resources