Meteor.js - Fetch/Get Enrollment token (from Accounts.sendEnrollmentEmail) - javascript

I can't figure out how to get the enrollment token from the Accounts.sendEnrollmentEmail function.
I know this function sends a direct mail towards the user which in the end looks something like this:
http://localhost:3000/#/enroll-account/FCXzBbqHInZgBlLaOpu8Iv11jP9DJEG-e1auAHDsh6S
However, I would need to somehow get only to the token part FCXzBbqHInZgBlLaOpu8Iv11jP9DJEG-e1auAHDsh6S as I want to send enrollment mail trough a different service (e.g Postmark)
How to do this?

The Accounts.sendEnrollmentEmail(userId, email) function generates a random token and saves it in the user's services.password.reset.token field.
The code that generates the token is:
var token = Random.secret();
var when = new Date();
var tokenRecord = {
token: token,
email: email,
when: when
};
Meteor.users.update(userId, {$set: {
"services.password.reset": tokenRecord
}});
(You can view the function's source code here).
It then sends an email to the user using the Email package. If you want to use a different service to send the email, you basically have 2 options:
Use the same convention yourself (i.e, create the same record and use your own email service in your own function).
Use the existing function, allow the mail delivery to fail silently and then query the user's document for the token and send the email yourself.
Neither is a particularly good option, but both will work for the time being. I wish they had refactored this part into its own function.
Note that the accounts packages are expected to undergo some changes towards the release of the next Meteor versions.
BTW, this function is very similar to Accounts.sendResetPasswordEmail, which you may also wish to override or create your own version.

Related

firebase javascript injection

I want ask something about firebase security. How to handle following situations?
User is creating account with createUserWithEmailAndPassword() function, then i save his username,email,created_at...to realtime db. But what if data are not saved correctly. His account is created and he is logged in automatically but data is not stored.
I have some registration logic... for example unique usernames... so before creating acc i check if this username exist in realtime db. But he still can call createUserWithEmailandPassword() from js console and account is created.
For situation one:
According to the firebase docs (https://www.firebase.com/docs/web/api/firebase/createuser.html), creating a user does not automatically authenticate them. An additional call to authWithPassword() is required first. In order to ensure that a user isn't authenticated without valid data, you could run a check to the server to make sure the data is saved correctly before authenticating.
Edit: Nevermind that; looks like firebase does auto-auth now - take a look at what I wrote below.
Now a concern with this approach would be if your app allowed people to authenticate with an OAuth provider like gmail, then there is no function for creating the user before authenticating them. What you may need to do is pull the user data from the firebase, determine if it's valid, and if its not valid show a popup or redirect that lets the user fix any invalid data.
For situation two:
If you wanted to make sure that in the case of them calling createUserWithEmailAndPassword() from the console a new user is not created, you could try something like this with promises;
var createUserWithEmailAndPassword = function(username, password) {
var promise = isNewUserValid(username, password);
promise.then(function() {
// Code for creating new user goes here
});
}
In this way, you never expose the actual code that makes a new user because it exists within an anonymous function.
I don't think that this could solve the problem entirely though because firebases API would let anyone create an account using something
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.createUser({
email: "bobtony#firebase.com",
password: "correcthorsebatterystaple"
}
(Taken from https://www.firebase.com/docs/web/api/firebase/createuser.html)
If you wanted to make sure that server side you can't ever create a user with the same user name, you'd need to look into firebases's rules, specifically .validate
Using it, you could make sure that the username doesn't already exist in order to validate the operation of creating a username for an account.
Here's the firebase doc on rules: https://www.firebase.com/docs/security/quickstart.html
And this is another question on stack overflow that is quite similar to yours. Enforcing unique usernames with Firebase simplelogin Marein's answer is a good starting point for implementing the server side validation.
First save the user credentials in the realtime database before you create the user:
var rootRef = firebase.database().ref('child');
var newUser = {
[name]: username,
[email]: useremail,
[joined]: date
};
rootRef.update(newUser);
After adding the Usersinfo into the realtime database create a new user:
firebase.auth().createUserWithEmailAndPassword(useremail, userpassword).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
When an error occured while inserting the data in the realtime database, it will skip the createuser function.
This works fine for me, hope this helps!

Automatically assign a customer to a specific customer group on sign-up - Bigcommerce

I've been told by BC support that this isn't possible, but I would be surprised if there really wasn't a way.
I need to be able to automatically assign a customer to a specific customer group when they create an account. My thought:
I would add an extra field to the sign-up form
Provide a user with a code (a string or number)
User enters code when creating new account
User hits submit
On form submit I would grab the value of the extra field:
var codeInput = document.getElementById('code-input').value;
I would then compare that value to a pre-defined string, and if there is a match, I would assign that customer to groupX (with a group id of 8):
if ( codeInput === "codeIGaveToTheUser" ) {
currentUserGroupID = 8;
}
Is it possible to assign a customer to a specific group on sign-up like this (or any other way)?
Any help is much appreciated.
Although using BigCommerce webhooks would ensure the highest success rate of executing your customer group assignment app, it requires quite a bit of setup on BigCommerce (creating a draft app, getting an oAuth key, jumping jacks, etc), and may be a bit of overkill for your requirements.
Here's an easier way, in my {mostly} humble opinion, that takes advantage of much of what you included in your original question. Any solution though will nonetheless require an external server to handle the customer group assignment through the BigCommerce API.
Within the BigCommerce control panel, add in the extra field to the user sign up form like you mentioned.
So as you can see, this new input field has been added natively to the default registration page:
So now, when a user creates an account on your site, the value for the Signup Code (the custom field created) will be directly accessible through the API for that customer's account. Take a look at what that JSON data looks like:
Okay, so this is nice and all, but how do we automate it?
To do so, we will have to let our external application know that a customer just registered. Furthermore, our external application will need some sort of reference to this newly created customer, so that it knows which customer to update the customer group for. Normally a BigCommerce webhook would notify us of all this, but since we aren't using a BigCommerce webhook, here's the alternative method to triggering the external script.
We will trigger our external application via the BigCommerce Registration Confirmation page - createaccount_thanks.html. This page is loaded immediately after a customer creates an account, so it is the perfect place to insert our trigger script.
Additionally, now that the customer is logged in, we can access the customer's email address via a BigCommerce Global system variable -%%GLOBAL_CurrentCustomerEmail%%.
We should make an HTTP request from this page to our external application along with the customer's email address. Specifically, we can make an XMLHttpRequest via JavaScript, or to be modern, we'll use Ajax via jQuery. This script should be inserted before the closing </body> tag on createaccount_thanks.html.
Example of POST request (although a GET would suffice as well):
<script>
$(function() {
$('.TitleHeading').text('One moment, we are finalizing your account. Please wait.').next().hide(); // Let the customer know they should wait a second before leaving this page.
//** Configure and Execute the HTTP POST Request! **//
$.ajax({
url: 'the_url_to_your_script.com/script.php',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({email:"%%GLOBAL_CurrentCustomerEmail%%"}),
success: function() {
// If the customer group assignment goes well, display page and proceed normally. This callback is only called if your script returns a 200 status code.
$('.TitleHeading').text('%%LNG_CreateAccountThanks%%').next().show();
},
error: function() {
// If the customer group assignment failed, you might want to tell your customer to contact you. This callback is called if your script returns any status except 200.
$('.TitleHeading').text('There was a problem creating your account').after('Please contact us at +1-123-456-7890 so that we can look into the matter. Please feel free to continue shopping in the meantime.');
}
});
});
</script>
Now finally, you just need to create your serverside application responsible for handling the request above, and updating the customer's customer group. You can use any language that you desire, and BigCommerce even offers several SDK's you can use to save mega development time. Just remember that you need to host it somewhere online, and then insert its URL to the JS script above.
PHP Example (quick & dirty):
git clone https://github.com/bigcommerce/bigcommerce-api-php.git
curl -sS https://getcomposer.org/installer | php && php composer.phar install
<?php
/**
* StackOverflow/BigCommerce :: Set Customer Group Example
* http://stackoverflow.com/questions/37201106/
*
* Automatically assigning a customer group.
*/
//--------------MAIN------------------------//
// Load Dependencies:
require ('bigcommerce-api-php/vendor/autoload.php');
use Bigcommerce\Api\Client as bc;
// Define BigCommerce API Credentials:
define('BC_PATH', 'https://store-abc123.mybigcommerce.com');
define('BC_USER', 'user');
define('BC_PASS', 'token');
// Load & Parse the Email From the Request Body;
$email = json_decode(file_get_contents('php://input'))->email;
// Execute Script if API Connection Good & Email Set:
if ($email && setConnection()) {
$customer = bc::getCollection('/customers?email=' .$email)[0]; //Load customer by email
$cgid = determineCustomerGroup($customer->form_fields[0]->value); //Determine the relevant customer group ID, via your own set string comparisons.
bc::updateCustomer($customer->id, array('customer_group_id' => $cgid)) ? http_send_status(200) : http_send_status(500); //Update the customer group.
} else {
http_send_status(500);
exit;
}
//-------------------------------------------------//
/**
* Sets & tests the API connection.
* #return bool true if the connection successful.
*/
function setConnection() {
try {
bc::configure(array(
'store_url' => BC_PATH,
'username' => BC_USER,
'api_key' => BC_PASS
));
} catch (Exception $e) {
return false;
}
return bc::getResource('/time') ? true : false; //Test Connection
}
/**
* Hard define the customer group & signup code associations here.
* #param string The code user used at signup.
* #return int The associated customergroup ID.
*/
function determineCustomerGroup($signupCode) {
switch ($signupCode) {
case 'test123':
return 1;
case 'codeIGaveToTheUser':
return 8;
default:
return 0;
}
}
So then you would do your customer group string comparisons directly in the serverside program. I'd recommend you rewrite your own BC API script as the one above in quality is really something along the lines of functional pseudo-code, but more so present to show the general idea. HTH
You would need to set up a server to listen for webhooks unless you wanted to do a cron job. We have some basic information on the developer portal, but I included more resources below. From there, you'd need to choose your server language of choice to listen for the webhooks once they been created, respond correctly (200 response if received), execute code based on this information, and then take action against the BC API.
So if you were looking for a code, you'd need to listen for the store/customer/created webhook, and have your code look for a custom field that contained the code. If it was present, then take action. Else, do nothing.
https://developer.github.com/webhooks/configuring/
http://coconut.co/how-to-create-webhooks
How do I receive Github Webhooks in Python

Meteor: Turning email address into MD5hash on server, and accessing on client

So I want to use Gravatar avatars on my website. I got the appropriate packages for it. The way it works, it turns email addresses into an "MD5Hash." That's sent to Gravatar in exchange for the image url.
Fine, but I want to display avatars without exposing everyone's email address. At the same time, I have users already that likely already have gravatars, and I think it would be cool if their avatars just popped up one day, without adding another field to the user profiles collection, or asking them to.
Is there a way to do some of this on the server and accomplish my goal?
Handlebars.registerHelper("gravatar", function(id){
var email = Meteor.users.findOne({_id: id}).emails[0].address;
var options = {
secure: true,
size: 29,
default: 'retro'
};
var md5Hash = Gravatar.hash(email);
// 5658ffccee7f0ebfda2b226238b1eb6e
var url = Gravatar.imageUrl(md5Hash, options);
// https://secure.gravatar.com/avatar/5658ffccee7f0ebfda2b226238b1eb6e
return url;
});
Hackish:
On the server:
userArray = Meteor.users.find(query,{fields: {"emails.address": 1}}).fetch();
userArray.forEach(function(el,i,a){
a[i] = { _id: el._id, md5hash: Gravatar.hash(el.emails[0].address) };
}
where query is whatever your criteria are, will get you an array of objects whose _id matches the _id of each user and whose md5hash value is the hash of that user's email. You can set up a method to return this array to you when you need it.
The good news is that your client can use these hashes to get avatars in whatever sizes might be necessary at any time.
Much less hackish:
The problem with the above is that your server is frequently going to be recomputing the md5hash of each email. Plus you're getting a potentially big and non-reactive array from the server. You'll live to regret this. You really just want to add an md5hash key to the emails array in the user document, initialize it for existing users, and make sure that new users always have this key set at creation time. This will let you handle either single-email address users or multiple-email address users.

Facebook API - access token for web application

I am making a web app that pulls the latest posts from our Facebook page and processes them.
This is all working fine with a hard-coded access token generated from this page.
The problem is that this token expires, so i am looking for a solution to generate a new token every time the page loads or a non-expiring token - (i have read somewhere that non expiring tokens don't exist anymore).
So of course i did some research, here, here and here.
But non of these examples seem to be working.
Before any complaints of some code that i have tried so far, this is my working example - with an expiring access token:
var Facebook = function () {
this.token = 'MYTOKEN';
this.lastPost = parseInt((new Date().getTime()) / 1000);
this.posts = [];
};
Facebook.prototype.GetPosts = function () {
var self = this;
var deffered = $q.defer();
var url = 'https://graph.facebook.com/fql?q=SELECT created_time, message, attachment FROM stream WHERE created_time < ' + self.lastPost + ' AND source_id = 437526302958567 ORDER BY created_time desc LIMIT 5&?access_token=' + this.token + '';
$http.get(url)
.success(function (response) {
angular.forEach(response.data, function (post) {
self.posts.push(new Post(post.message, post.attachment.media, post.attachment.media[0].src, post.created_time, 'facebook'));
});
self.lastPost = response.data[response.data.length -1].created_time;
deffered.resolve(self.posts);
self.posts = [];
});
return deffered.promise;
};
return Facebook;
Any help / suggestion will be greatly appreciated.
First off, it is important to remember that Facebook has just launched the Version 2 of the Graph API. From April 2014 on, if you have issues with your app, you need to tell us when you created it on Facebook Developers (new apps use the Version 2 by default).
In order manage pages, your app needs to have manage_pages permission. Make sure that the user you want to manage fan pages for has authorized you. If your app uses the Version 2, make sure that Facebook (the Facebook staff) has authorized you to ask users that kind of permission, otherwise your app won't work.
Once you get your token, exchange it for a permanent token (or a token with long expiry date). Make sure you use the token of the fan page, not the token of the user.
If instead you want to read the stream of public fan pages, you need an access token with read_stream permissions. This permission needs to be approved by Facebook (see above) and this specific type of permission takes time to approve, if you're using the Version 2 of the Graph API. If you're using the old API (Version 1), you can still do that without pre-approval on Facebook's side.
The URL to ask for the permission to read the stream is as follows: https://www.facebook.com/dialog/oauth?client_id=$YOUR_APP_ID&redirect_uri=$YOUR_URL&scope=read_stream,manage_pages (i've added manage_pages in this case, you may not need it).
That url will prompt for authorization. Once the user has authorized the app, you'll be recirected to the URL you chose, with a code= variable.
At that point, call this other url:
https://graph.facebook.com/oauth/access_token?client_id={$app_id}&redirect_uri=$someurl&client_secret={$app_secret}&code={$code}
You'll get a response that has the access_token=variable in it. Grab that access token, exchange it for a long one, with the following URL:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={$app_id}&client_secret={$app_secret}&fb_exchange_token={$token_you_have_just_grabbed}
The response will give you a token that lasts for some time. Previously, Facebook had decided to have these "long duration tokens" expire after one month. I have found out, though, that they may have changed their mind: if you put a user token in the debugger, you'll see it never expires.
This is the authorization flow for users who visit with a browser. There's the app authorization flow too. If all you need is a stream from your own Fan page, you want to do the following (with Graph API V.1):
make an HTTP GET request using the following URL:
https://graph.facebook.com/oauth/access_token?type=client_cred&client_id={$app_id}&client_secret={$app_secret}
Use the resulting token to make another HTTP GET call, like so:
https://graph.facebook.com/{$your_page_id}/feed?{$authToken}&limit=10
//ten posts
Decode the json object
You're done.

How to pass two parameters to model.fetch() in backbone?

I'm making a very simple user authentication system. I'm trying to pass a combination of username and password as params to the back-end nodejs server. So this combination will be used in my db query to fetch user details.
This is what I tried on the front-end:
var user = new UserModel({id:['username', 'password']});
user.fetch();
I have defined a urlRoot property in my model that goes like this: /api/users
The back-end will handle the following url: /api/users/:id
Here since I have passed id as an array, I tried to access the 'username' by doing this req.params.id[0]. Instead it returns the first letter of the 'username'. But I want to take the entire string of username. Of course I could use the split() function to separate them but I believe there is a better way to do this.
Please tell me if my approach is wrong somewhere.
That's because Backbone serializes your array to string and then encodes it as URI component.
So effectively you're sending a String 'username%2Cpassword' instead of an array.
I had the same problem and decided that sign in process doesn't really represent any "physical" resource, and most likely shouldn't be handled by user model. One doesn't CRUD users when signing in.
What i did was to create a separate model for SignIn:
SignInModel = Backbone.Model.extend({
urlRoot: 'api/sign_in',
defaults: {
'username' : '',
'password': ''
}
});
which statically maps to api/sign_in (no id's here), and then query the database by username and password passed in the request body to the api/sign_in handler.
UserModel can then be left to handle CRUD of users.

Categories