How to generate new Meteor login tokens (server side) in order to make a quick login link - meteor

Meteor has a loginWithToken method, and there are resume tokens in the user object. So one can login using one of these tokens with loginWithToken. That works.
Is there a way to generate new login tokens, or should I just use the resume tokens to create a quick login link?

As Johnny said, you can use the Accounts._generateStampedLoginToken() function, which is actually nothing special, just the following function:
_generateStampedLoginToken = function () {
return {
token: Random.secret(),
when: new Date
};
}
anyway, to use it, here is an example:
// Server //
// Creates a stamped login token
var stampedLoginToken = Accounts._generateStampedLoginToken();
/**
* Hashes the stamped login token and inserts the stamped login token
* to the user with the id specified, adds it to the field
* services.resume.loginTokens.$.hashedToken.
* (you can use Accounts._hashLoginToken(stampedLoginToken.token)
* to get the same token that gets inserted)
*/
Accounts._insertLoginToken(user._id, stampedLoginToken);
// Client //
// Login with the stamped loginToken's token
Meteor.loginWithToken(stampedLoginToken.token);

Yes, you can generate new tokens by calling Accounts._generateStampedLoginToken(). You can call it from within a login handler.
https://github.com/meteor/meteor/blob/master/packages/accounts-base/accounts_server.js#L114

it's 2015 - use one of these packages:
poetic:accounts-passwordless
acemtp:accounts-passwordless
http://fastosphere.meteor.com/?q=passwordless

Related

Sending Verification email to existing users

I am working on a web app with an existing user base. Email verification was not initially implemented in the sign in flow.
I have successfully added code for sending verification email for all new sign ups but I also wanted to make a small page (or modal) where current users would be shown a button that would send the verification link to their inbox
The current sign up flow where I created the user with createUserWithEmailAndPassword I was able to get access to the user.user.sendEmailVerification method to do so, but cannot find any way to access this method to implement the feature for existing users.
Is there a way to access the sendEmailVerification method after the user has been created?
I am assuming that it would be available within the onAuthStateChange trigger but implementing that would lead to a bad UX (as I do not want to prompt the users everytime they login)
Edit:
I know the documentation states that we can use the firebase.auth().currentUser to get the current user but that, for some reason did not work.
Also, I found references online suggesting to no longer use that method and they mentioned to use the onAuthStateChange method instead, which is why I was looking into that approach
You can try this method:
const btnVerifyEmail = document.getElementById("btn-verify-id")
btnVerifyEmail.onclick = function () {
const user = firebase.auth().currentUser;
user.sendEmailVerification().then(function() {
// Email sent.
console.log("Email Sent")
}).catch(function(error) {
// An error happened.
console.log(error)
});
}
It's mentioned in the documentation right here
The sendEmailVerification() should not be called in the onAuthStateChanged event because it would blast out an email on every page load if the user's email isn't verified.
You should instead display a notification on the page if User.emailVerified is false that contains a link to send the user an email.
Here's a working example:
// On page load watch for auth state changes
firebase.auth().onAuthStateChanged(function(user) {
// If the user is logged in
if (user) {
// If the user's email isn't verified
if (!user.emailVerified) {
// Show the notification bar that informs the user that they need to validate
// their email by clicking a link. Let's pretend the link looks like this:
// Send me a verification email
showNotification();
}
}
});
// Function attached to your link's onclick event
function sendEmailVerification() {
// Retrieve the current user
const user = firebase.auth().currentUser;
// If user's email is already verified, exit
if (user.emailVerified) {
return;
}
// Tell Firebase to send the verification email and discard the promise
user.sendEmailVerification().then().catch();
}
Dharmaraj's answer is good but this is a full example.

How to get Google UserId from active user session in App Maker?

Is there a way to get "User Google Id" from the session in App Maker. In the documentation its only mentioned how to retrieve the email of the logged in user Session.getActiveUser().getEmail() but no where it says how to get the id. I need this because the user email might sometimes changes. So I need the user id to keep track of users and related permission tasks. Or is there something I'm missing out here in how this should be implemented.
Yet an easier way to find Google Id simply using the Directory model. Although its mentioned in documentation that there is a way to get current signed in user id ( which is Google Id), its not clearly stated how - maybe documentation could be improved here. Another problem is that in many occasions the email of current active user is referred to as the id for example in deprecated method Session.getActiveUser().getUserLoginId(). Anyways this is a proper way to get the id.
var query = app.models.Directory.newQuery();
query.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
var result = query.run();
var GoogleId = result[0]._key;
So with this GoogleId you can safely relate different models with each other and not worry that database integrity might break if an already referenced user email is changed.
Relating the different models could be done simply by creating a model that acts as a wrapper model around the Directory model and storing GoogleId in it. Then linking that model to other models where you want to track user related data because unfortunately we can not directly link The Directory Model to other models.
A team member has figured it out. This should be done using Apps Script - which works within App Maker environment using server side script.
var GoogleUser = (function (){
/**
*
* #param {string} email
*/
function getUserObjByEmail(email){
// Same as using AdminDirectory class.
var apiUrl = "https://www.googleapis.com/admin/directory/v1/users/"+email+"?fields=id";
var token = ScriptApp.getOAuthToken();
var header = {"Authorization":"Bearer " + token};
var options = {
"method": "GET",
"headers": header
};
var response = JSON.parse(UrlFetchApp.fetch(apiUrl, options));
return response;
}
/**
*
* #param {string} email - User email.
*/
function getIdByEmail(email){
return getUserObjByEmail(email)['id'];
}
var publicApi = {
getIdByEmail: getIdByEmail
};
return publicApi;
})();
Note that using var apiUrl = "https://www.googleapis.com/admin/directory/v1/users/"+email+"?fields=id"; is not going to be asynchronously called because its already happening in the server.
Is this a dup of this question?
I think this will solve your problem, even though it's a bit of a hack.

meteor-shopify User Creation/ Login after Auth callback

Assuming I want to create users upon authorizing the app, how would I grab their email during the onAuth callback...? Looks like the callback assumes the user is already logged in. Am I thinking about it correctly?
I noticed when installing the Fishbowl Prizes app, after auth I can click on the accounts tab and see that all my account info is pre-populated from my shopify store account (name, email, address, etc).
I'm not sure if I should go by the title or the content of the post in terms of answering your question, so I'll provide a very simple example of how to get the info from the API and do something with it here.
I have provided a more in depth answer related specifically to grabbing the details from the API for user account creation here: https://github.com/froatsnook/meteor-shopify/issues/15#issuecomment-177413630
Looks like the callback assumes the user is already logged in.
The userId param is undefined if there is no user. If your onAuth operations don't need to do anything with the user, you can just leave it out of the params. In your case you'll just want to handle it conditionally using an if/else block:
if(!userId){
// do stuff
} else {
// do other stuff
}
On to the example of grabbing those details from the API:
All the prepopulated information you are seeing is available from the Shopify API in the shop object. You already have the access token when onAuth callbacks are fired, so you can just grab it from the API immediately after you have inserted the shop's Keyset.
For the sake of simplicity, in this example we'll assume the user already exists and is logged in. In your server-side onAuth callback (after you have inserted the keyset) you can do something like this to add those fields to the user's profile object:
Shopify.onAuth(function(access_token, authConfig, userId) {
var shopUUID = uuid.new(); // Not secure to name keyset same as the shop!
Shopify.addKeyset(shopUUID, {
access_token: access_token
});
var api = new Shopify.API({
shop: authConfig.shop,
keyset: shopUUID
});
// get the Shop object from the API
var shopObj = api.getShop();
var userInfo = {
'profile.name': shopObj.shop_owner,
'profile.email': shopObj.email,
'profile.phone': shopObj.phone,
'profile.shopName': shopObj.name
};
Meteor.users.update({_id: userId}, {$set: userInfo})
});
Then you can use them in templates like this:
{{currentUser.profile.name}} or {{currentUser.profile.email}}
Or in functions like so:
var realName = Meteor.user().profile.name
or
var userEmail = Meteor.user().profile.email etc
For a more about using this data for user creation, see my explanation here:
https://github.com/froatsnook/meteor-shopify/issues/15#issuecomment-177413630

Show a loader during the user log in process

I'm using the Accounts API to manage users. My app first tries to log in a user using their credentials, and in case that results in an error, it creates a new user account using the input credentials.
// Log the user in
Meteor.loginWithPassword(username, token, function(error) {
if(error) { // create a new user account, log them in and show the page
Accounts.createUser({
username: username,
email: username + '#example.com',
password: token,
profile: {name: username}
}, showThePage);
}
else { // show the page
//showThePage();
window.location.reload();
}
});
But this code block executes only when the user was previously logged out from their browser, and if that is the case it takes 2-3 seconds for Meteor to log the user in using loginWithPassword. As I'm using v0.5.0, there is no Meteor.loggingIn(), and the only thing I have is Meteor.userLoaded(). Meteor, for some reason, performs the login operation twice -- once by loading a placeholder user (that has only its userId property set) and again by loading the actual user. This makes userLoaded() return true twice, because of which my loader image doesn't work as expected.
Also notice that in the else block inside loginWithPassword, I'm doing a window reload. I've a function showThePage() which contains all the template data & event binding code. That function retrieves data using the username of the logged in user. Now because when that function in else block executes there isn't a real user logged in (remember meteor takes time to log the user in), no data gets fetched.
Is there a workaround for this problem?
First of all Meteor.userLoaded goes away after you upgrade beyond 0.5.0. You should check if Meteor.userId() === null to know if the user login has completed, which works in 0.5.0 and beyond. It may get called multiple times, as you have noted, but only when it has a real value has the login completed.
If you really can't update to 0.5.1, use a session variable to store loggingIn in between the call to loginWithPassword and the callback.
Session.set('loggingIn',true);
Meteor.loginWithPassword(...
Session.set('loggingIn',false);
});
Then, use the Session.get('loggingIn') call where appropriate.
Want to adapt userLoaded() instead?
var userLoadedTimes = 0; // can't use session variable because it would mess up reactive context on increments
Session.set('loggingIn',false);
Meteor.autorun(function () {
var userLoaded = Meteor.userLoaded(); // reactive.
if (userLoaded)
userLoadedTimes++;
if ((userLoadedTimes % 2 == 0) && (userLoadedTimes != 0))
Session.set('loggingIn',true);
else
Session.set('loggingIn',false);
});
What's that modulo doing there? Well if userLoaded has called a reactive context twice for some reason, you have actually logged in. So we check if userLoadedTimes is a multiple of two/even. All other times, i.e., when userLoadedTimes is odd (userLoadedTimes % 2 == 1), we're looking at the fake user... which means we're still loading the real user!
If this doesn't work, apply the even/odd logic to the first solution using session variable changes on the callback, in the event Meteor calls the loginWithPassword callback twice.

Is there a post createUser hook in meteor when using accounts-ui package?

Let's say I have a todo app, and I want to make sure that every user that registers has at least one todo to start with, something like "First todo to cross off!", how would I do that in meteor?
In general, the way I see it, I can do it when the user is created for the first time (ideal), or check to see whether they need a new todo every time they log in (less ideal). In the latter case, I can do a check for Todos.findOne(), and if the count is 0, add one. However, seems that whether I do this in my router when the page loads, or on some template's .rendered function, the collection I'm checking hasn't been loaded yet, so I always create a new todo, even if one really does exist. So it'd be great if someone could explain how to get around that.
But, what I'd ideally want is the ability to just create a new Todo when the user is created. There is a Accounts.onCreateUser method, but that is used to add additional info to user profile, not a post-create hook. There's also a method to programmatically create the user using Accounts.createNewUser with a callback, but I'm using the accounts-ui package so am not programmatically adding users. In a less ideal case, I could check for the Todo whenever the user logs in, but even in that case, there seems to be a federated Accounts.loginWithXService login, so not sure how to handle the callback when any user logs in, regardless of service type.
I think I must be missing something simple, so apologies if this is super obvious. Any help is appreciated.
The Meteor API now has the hook onCreateUser:
Accounts.onCreateUser(function (options, user) {
Todos.insert({
owner: user._id,
text: "First todo to cross off!",
});
// We still want the default hook's 'profile' behavior.
if (options.profile)
user.profile = options.profile;
return user;
});
I used the _.wrap method described above but wanted to include an additional suggestion. It's a good idea to call the original callback from your new custom callback. Meteor does some things on the callback that we don't want to miss.
Modified code that worked like a champ for me:
Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0];
origCallback = args[1];
var newCallback = function(error) {
// do my stuff
origCallback.call(this, error);
};
createUser(user, newCallback);
});
If you are using the UserAccounts package: postSignUpHook now exists.
Splendido just merged my pull request for exactly this issue.
AccountsTemplates.configure({
/*...*/
postSignUpHook: /*[callback with your actions post full user creation goes here]*/,
/*...*/
}
Documentation (You'll need to scroll down it's the last hook):
func(userId, info) Called, server side only, just after a successfull user account creation, post submitting the pwdForm for sign-up: allows for custom actions on the data being submitted after we are sure a new user was successfully created. A common use might be applying roles to the user, as this is only possible after fully completing user creation in alanning:roles. The userId is available as the first parameter, so that user object may be retrieved. The password is not available as it's already encrypted, though the encrypted password may be found in info if of use.
You can piggyback onto functions that are called by Meteor by wrapping them. I'm also using the accounts-ui and accounts-password packages and I use Underscore's _.wrap method to redefine the loginWithPassword function. Underscore is included in Meteor by default.
I use something like this for logging in:
Meteor.loginWithPassword = _.wrap(Meteor.loginWithPassword, function(login) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0],
pass = args[1],
origCallback = args[2];
// Create a new callback function
// Could also be defined elsewhere outside of this wrapped function
var newCallback = function() { console.info('logged in'); }
// Now call the original login function with
// the original user, pass plus the new callback
login(user, pass, newCallback);
});
In this specific case, the code above would go in your client code somewhere.
For Accounts.createUser, it might look something like this (also somewhere in client code):
Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {
// Store the original arguments
var args = _.toArray(arguments).slice(1),
user = args[0],
origCallback = args[1];
// Create a new callback function
// Could also be defined elsewhere outside of this wrapped function
// This is called on the client
var newCallback = function(err) {
if (err) {
console.error(err);
} else {
console.info('success');
}
};
// Now call the original create user function with
// the original user object plus the new callback
createUser(user, newCallback);
});
Hope that's helpful.
One of the Meteor devs answered this question in Meteor google group: https://groups.google.com/forum/?fromgroups=#!topic/meteor-talk/KSz7O-tt4w8
Basically, right now, there is no createUser hook when using accounts-ui, only when programmatically doing so via Accounts.createUser. Also, there are no hooks for login, unless using the lower-level login functions like loginWithFacebook, etc. I haven't figured out an ideal way around this yet, but a few ways of handling it:
if needing to enter a default value into a collection, in that collection's subscription, use the onComplete argument. In this callback, if there are no entries in collection, add one. This avoids the first problem I mentioned in my post about not knowing when a collection was loaded, though not ideal since collection could be empty because user already removed first default one:
Meteor.subscribe 'todos', user: Meteor.userId(), () ->
todo = Todos.findOne()
unless todo
Todos.insert user: Meteor.userId()
you can set up a login hook by using the Meteor.autorun reactive method to check for a change in Meteor.userId(). That'll only get called when the user logs in/reloads the page. This is more useful for non-collection stuff since the collection is not guaranteed to be loaded when Meteor.userId is set:
Meteor.autorun () ->
if Meteor.userId()
console.log 'Do some post login hook'
So I think the efficient solution is still out there somewhere, but wanted to update this post with workarounds I had found in the meantime.
I think this answer this question better: How can I create users server side in Meteor?
in resume:
Accounts.createUser({
username: username,
email : email,
password : password,
profile : {
//publicly visible fields like firstname goes here
}
});
check the meteor docs for more: http://docs.meteor.com/#/full/accounts_createuser

Resources