I am working on a website, where we want user to login using Javascript and not using
postback. I have few pages which user can view without logging in, but on these pages
if he wants to do something like "Add to favourite" or "Report abuse" or similar, he has to log in. I can display a div where he can log in. But I want the system to perform the task
he initially tried to do. So say if the user wants to perform "Add to favourite", he should first log in and on success othere function "Add to favourite" should be called. So logic
should know where to delegate once user is logged in.
As this loging stuff is required for many other purposes too, so I can hard code one function once log on is successful. I need something like delegation which Login Routine
should know so that it calls it back.
Help will be appritiated.
Regards
Parminder
This assumes you have some basic experience with AJAX and callbacks...
// When the user submits the login form, call the login function with
// the original call as the third parameter
function login(username, password, callback) {
// Perform AJAX call with username and password.
// Your AJAX utility should call loginResult as its callback
// Store the callback parameter on your object somewhere
}
function loginResult(result, callback) {
if (/* Check if the result contains a valid user or sessionID, etc. */) {
// Logged in, yay. Do stuff to the UI to show this.
if (callback) callback();
} else {
// Error logging in, oh no. Display error.
}
}
You could store the lastAttemptedAction in the session, and always check this upon a successful login. So the order of events would be:
User accesses site anonymously
User clicks "add to favorite"
"Add to favorite" is added to Session.lastAttemptedAction
User is asked to login
User successfully logs in
Session.lastAttemptedAction is performed/cleared
If nothing is stored in the lastAttemptedAction, then nothing will be ran. The user will silently login, and continue on his or her merry way.
Related
I'm currently checking whether a user is logged in client-side using:
firebase.auth().onAuthStateChanged(function (user)
{
}
However, this method takes 1-2 seconds after the page has loaded to be called and I was wondering whether there was a faster way to check if the user is signed in?
I'm using this to check that the user is authorised to enter a specific page on a website, and at the moment they are able to view the contents of it before being redirected to the homepage, which shouldn't happen. So I am hoping for a faster alternative.
You can try this method that i was using to execute certain code immediately after the user logged (avoid waiting by executing a fast loop that keeps testing for the user data) :
var int = setInterval(()=>{
firebase.auth().currentUser ?
// you can do whatever you want immediately when the user is logged
// and then clear the interval;
clearInterval(int)
: ''
}
},10)
I was wondering if someone would be kind enough to provide a meteorpad or code example of using one of the methods listed above properly in Meteor (with iron:router). I'm struggling to understand how exactly these methods interact with my app, and it seems these methods are new enough that there isn't much good documentation on how to use them correctly. Thanks!
http://docs.meteor.com/#/full/Accounts-onResetPasswordLink
Ok, so I am going to post what I ended up learning and doing here so others can use it as a reference. I'll do my best to explain what is happening as well.
As can be seen in the other comments, the 'done' function passed to the Accounts.on****Link callback was the main part that tripped me up. This function only does one thing - re-enables autoLogin. It's worth noting that the 'done' function/autoLogin is a part of one of the core 'accounts' packages, and cannot be modified. 'autoLogin' is used in one particular situation: User A tries to reset his or her pw on a computer where User B is currently logged in. If User A exits the reset password flow before submitting a new password, then User B will remain logged in. If User A completes the reset password flow, then User B is logged out and User A is logged in.
The pattern used to handle 'done' in the accounts-ui package, and what I ended up doing, assigns 'done' to a variable that can then be passed to your template event handler function, and run once your reset password logic is complete. This variable assignment needs to be done in the Accounts.on****Link callback, but the callback can be placed in any top-level client side code (just make sure you assign the scope of the variables correctly). I just put it at the start of my reset_password_template.js file (I've only done this for resetting passwords so far, but the pattern should be similar):
client/reset_password_template.js:
// set done as a variable to pass
var doneCallback;
Accounts.onResetPasswordLink(function(token, done) {
Session.set('resetPasswordToken', token); // pull token and place in a session variable, so it can be accessed later
doneCallback = done; // Assigning to variable
});
The other challenge of using these on****Link callbacks is understanding how your app 'knows' the callback has been fired, and what needs to be done by the app. Since iron:router is so tightly integrated with Meteor, it's easy to forget it is a separate package. It's important to keep in mind these callbacks were written to operate independently of iron:router. This means when the link sent to your email is clicked, your app is loaded at the root level ('/').
***Side note - There are some other answers here on StackOverflow that offer ways to integrate with iron:router, and load a specific route for each link. The problem for me with these patterns was that they seemed a bit hackish, and not in line with the 'meteor' way. More importantly, if the core Meteor team decides to alter the path of these registration links, these routes would break. I tried calling Router.go('path'); in the on****Link callback, but for some reason this didn't work in Chrome and Safari. I would love to have a way to handle specific routes for each of these emailed links, thus eliminating the need for constantly setting and clearing Session variables, but I couldn't think of a good solution that worked.
Anyways, as #stubailo described in his answer, your app is loaded (at the root level), and the callback is fired. Once the callback is fired, you have your session variable set. You can use this session variable to load the appropriate templates at the root level using the following pattern:
client/home.html (or your landing page template)
{{#unless resetPasswordToken}}
{{> home_template}}
{{else}}
{{> reset_password_template}}
{{/unless}}
With this, there are few things you need to take care of in your reset_password_template.js file, and home.js:
client/home.js
// checks if the 'resetPasswordToken' session variable is set and returns helper to home template
Template.home.helpers({
resetPasswordToken: function() {
return Session.get('resetPasswordToken');
}
});
client/reset_password_template.js
// if you have links in your template that navigate to other parts of your app, you need to reset your session variable before navigating away, you also need to call the doneCallback to re-enable autoLogin
Template.reset_password_template.rendered = function() {
var sessionReset = function() {
Session.set('resetPasswordToken', '');
if (doneCallback) {
doneCallback();
}
}
$("#link-1").click(function() {
sessionReset();
});
$('#link2').click(function() {
sessionReset();
});
}
Template.reset_password_template.events({
'submit #reset-password-form': function(e) {
e.preventDefault();
var new_password = $(e.target).find('#new-password').val(), confirm_password = $(e.target).find('#confirm-password').val();
// Validate passwords
if (isNotEmpty(new_password) && areValidPasswords(new_password, confirm_password)) {
Accounts.resetPassword(Session.get('resetPasswordToken'), new_password, function(error) {
if (error) {
if (error.message === 'Token expired [403]') {
Session.set('alert', 'Sorry, this link has expired.');
} else {
Session.set('alert', 'Sorry, there was a problem resetting your password.');
}
} else {
Session.set('alert', 'Your password has been changed.'); // This doesn't show. Display on next page
Session.set('resetPasswordToken', '');
// Call done before navigating away from here
if (doneCallback) {
doneCallback();
}
Router.go('web-app');
}
});
}
return false;
}
});
Hopefully this is helpful for others who are trying to build their own custom auth forms. The packages mentioned in the other answers are great for many cases, but sometimes you need additional customization that isn't available via a package.
I wrote this method, so hopefully I can give a good example of how to use it.
It's meant to be in conjunction with Accounts.sendResetPasswordEmail and Accounts.resetPassword (http://docs.meteor.com/#/full/accounts_sendresetpasswordemail and http://docs.meteor.com/#/full/accounts_resetpassword).
Basically, let's say you want to implement your own accounts UI system instead of using the accounts-ui package or similar. If you want to have a password reset system, you need three things:
A way to send an email with a password reset link
A way to know when the user has clicked the reset link
A method to actually reset the password
Here is how the flow should work:
The user clicks a link on your page that says "Reset password"
You find out which user that is (possibly by having them enter their email address), and call Accounts.sendResetPasswordEmail
The user clicks the reset password link in the email they just received
Your app is loaded and registers a callback with Accounts.onResetPasswordLink
The callback is called because the URL has a special fragment in it with the password reset token
This callback can display a special UI element that asks the user to input their new password
The app calls Accounts.resetPassword with the token and the new password
Now the user is logged in and they have a new password
This is a little complicated because it is the most advanced and custom flow possible. If you don't want to mess around with all of these callbacks and methods, I would recommend using one of the existing accounts UI packages, for example accounts-ui or https://atmospherejs.com/ian/accounts-ui-bootstrap-3
For some example code, take a look at the code for the accounts-ui package: https://github.com/meteor/meteor/blob/devel/packages/accounts-ui-unstyled/login_buttons_dialogs.js
Per the documentation:
You can construct your own user interface using the functions below, or use the accounts-ui package to include a turn-key user interface for password-based sign-in.
Therefore, those callback are for rolling your own custom solution. However, I would recommend using one of the following packages below, with accounts-entry being my preferred solution:
Use a combination of accounts-password and accounts-ui
Or use https://atmospherejs.com/joshowens/accounts-entry, especially if you want OAuth integrations such as Facebook, Twitter, etc. For handling email verification with this package, please see this Github issue.
It's been a year since this question but I just came up with the same problem.
Following your solution, what I found is that you could use the Session variable within the router and the onAfterAction hook to achieve the same, but using routes:
Router.route('/', {
name: 'homepage',
action: function() {
if (Session.get('resetPasswordToken')) {
this.redirect('resetPassword', {token: Session.get('resetPasswordToken')});
} else {
this.render('home');
}
}
});
Router.route('/password/reset/:token', {
name: 'resetPassword',
action: function () {
this.render('resetPassword');
},
data: function() {
return {token: this.params.token};
},
onAfterAction: function () {
Session.set('resetPasswordToken', '');
}
});
Of course, you will need also:
Accounts.onResetPasswordLink(function(token, done){
Session.set('resetPasswordToken', token);
doneResetPassword = done;
});
I'm using a publish to limit which data a user sees in their report
Meteor.publish("companyReport", function(){
if(!this.userId) return null;
var user = Meteor.users.findOne(this.userId);
var userCompany = user.profile.company;
var userRole = user.roles;
var userName = user.username;
if(function(){Roles.userIsInRole(Meteor.user(), ['chiefs']);})
{return ReportCollection.find({companyName:userCompany});}
else if (function(){Roles.userIsInRole(Meteor.user(), ['managers']);})
{return ReportCollection.find({companyName:userCompany, managerName:userName});}
else
{return null;}
});
Now, on the client side i can simply subscribe. It's generally working fine,except:
I noticed the following odd behaviour i don't want: if i am logged-in as a "chiefs" role user, and can see a company-wide report, if i logout and then login as a "managers" user, i can still see the data i am not supposedly allowed to see.
i know that my publications control mechanism is working ok, as when i login as a manager from another browser, i see the correct results (more limited)
i can only conclude that minimongo on the client is keeping the collection data from the old subscription as a "chiefs" user.
is this correct? if so, how do i flush this data upon logout? or?
what do people usually do to avoid this?
thanks
You probably do need to flush this data on logout, which will involve saving the subscription handle and then stopping it:
// when you subscribe
var reportHandle = Meteor.subscribe('companyReport');
// then when you want to log out
reportHandle.stop();
Meteor.logout();
UPDATE
If I understand your question, you want to make sure you're only ever flushing the minimongo when the user actually logs out:
Meteor.logout(function(err) {
if (err)
console.log(err);
else
reportHandle.stop();
});
ANOTHER UPDATE
Ah, now I see what you're asking. It depends on the package, as there's no onLogout event listener you can use for this. You can probably monkey-patch something quite easily, but I'm not sufficiently familiar with accounts-ui-bootstrap to say for sure.
When a user logs into a Meteor application a session is created. How long does it take for the session to expire after the user has closed the browser?
Does the session expire even if the browser is not closed?
Is it possible to react to the closing of a session? By invoking a callback for example.
I was looking for stale session / session timeout functionality for a meteorjs app and ran across this answer when looking for a suitable package to use.
Unfortunately the meteor-user-status package mentioned by Andrew doesn't seem to do a timeout.
I continued to look, found a couple of other packages, but couldn't get them to work for me - so I wrote a very small and simple package inspired by the others to do exactly what the questioner is asking for here i.e. force a user log out after a defined period of inactivity (whether the browser is open or not).
It does not, however, provide a callback (as it's the server that forces the logout) but this could probably be done with a Dep.autorun looking at Meteor.userId().
You can try it by typing
mrt add stale-session
And find details of how it works and how it can be configured here:
https://atmosphere.meteor.com/package/stale-session
and the code is open sourced here:
https://github.com/lindleycb/meteor-stale-session
Use the package I created that tracks user status, both overall and in several different browser sessions:
https://github.com/mizzao/meteor-user-status
With this, you can react to both sessions being closed and users logging out (see README). I've implemented it only for logged-in users, but you can do something similar if you want to track anonymous users.
I've been using zuuk:stale-session and I too initially wished it had a callback, but I solved it with an elegant solution (IMHO).
My app has a login template that get's rendered when if (! Meteor.user()) is true. It used to just run this.render('login') template which sufficed, but it still left the logged-in menu structure available. So, I switched to to Router.go('login') which has it's own layoutTemplate. So now when inactivity triggers the stale-session to delete the tokens for the user, the page goes to /login rather than just rendering the login template within whatever route was left stale.
Here's my code in router.js:
/** [requireLogin - make sure pay area is walled off with registration] */
var requireLogin = function() {
if (! Meteor.user()) {
// If user is not logged in render landingpage
//this.render('login');
Router.go('login');
this.next();
} else {
//if user is logged in render whatever route was requested
this.next();
}
}
/**
* Before any routing run the requireLogin function.
* Except in the case of "landingpage".
* Note that you can add more pages in the exceptions if you want. (e.g. About, Faq, contact...)
*/
Router.onBeforeAction(requireLogin, {
except:['terms','privacy','about','features','home']
});
I used following in my application to find the list of logged in user. It gives the list of logged in user but when i close the browser and clear the browser history, it shows the user isonline status true. Please can any one tell me where i am wrong?
When User signIn am validating user and redirect to url,
if (user != null)
{
if (Membership.ValidateUser(user.UserName, txtUserPassword.Text))
{
FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
}
}
List of logged in user code
MembershipUserCollection allUsers = Membership.GetAllUsers();
MembershipUserCollection filteredUsers = new MembershipUserCollection();
bool isOnline = true;
foreach (MembershipUser user in allUsers)
{
// if user is currently online, add to gridview list
if (user.IsOnline == isOnline)
{
filteredUsers.Add(user);
}
}
MembershipUser.IsOnline is not a very accurate check of whether or not a user is online. From the MSDN, a user is determined to be online:
if the current date and time minus the
UserIsOnlineTimeWindow property value is earlier than the
LastActivityDate for the user.
This means the user could log in and then log out 10 seconds later and still be considered online.
So in your case you were logging in, checking the count, logging out, closing the browser, clearing cache, etc., but the user's LastActivityDate still fell within the assumed window described above. What you can do is:
shorten the time window (which can produce some unwanted side
effects)
when logging the individual out, update their LastActivityDate to a
time that would make the above check invalid (e.g., the current time
UserIsOnlineTimeWindow)
update the LastActivityDate manually at certain key points (e.g.,
if your site is bookstore, maybe updating the LastActivityDate when
they add an item to the cart)