Code Snippets: Part 3

Now that b4YouGo has been launched, it’s time for my next project. As a result of this new endeavour, I’ll be learning much more about developing on Meteor.

1. Setting up an accounts system

An accounts system can be complicated, but it needn’t be. By installing the following packages, I was able to get up and running pretty fast (with some tweaking, see below):

useraccounts:core is actually a dependency that will get automatically downloaded when you install the useraccounts:foundation package, but due to some of the tweaking I had to do, I had to download it as a custom package.

Templates

At the minimum, you’ll need to create a login and registration template. And if you are doing a webapp, I suggest also a dashboard (or home) template so that your logged-in users can get redirected there instead of your main landing page.

In your login template, make sure you have this:

{{> atForm state='signIn'}}

In your registration template, make sure you have this:

{{> atForm state='signUp'}}

In the nav bar, if you want a smart sign-in/sign-out button, then you’ll need to add this:

{{> atNavButton}}

Routing

Great, now comes the hard part. We have to set up the actual routes like this in our router.js:

// Login and Register Routes
AccountsTemplates.configureRoute('signIn', {
    name: 'login',
    path: '/login',
    template: 'siteLogin',
});
AccountsTemplates.configureRoute('signUp', {
    name: 'register',
    path: '/register',
    template: 'siteRegister',
    redirect: '/',
});

This takes care of the login and registration templates. Obviously this is assuming that your login and registration templates are named siteLogin and siteRegister respectively.

When a user is logged in and they try to access your root domain, you’d probably want to redirect them to the dashboard rather than the landing page. To do that, I used this code:

Router.route('/', {
  name: 'landingPage',
  layoutTemplate: 'landingLayout',
  onBeforeAction: function (pause) {
      if (Meteor.user()) {
        Router.go('/home'); // send user to the dashboard
      } else {
        this.next();        // continue to landing page
      }
    }
});

Notice that I also have a special layout for it, called landingLayout. The dashboard route is also at the /home path, so I simply tell it to route there if there is a user logged in.

As for the dashboard, or in our case the /home path:

Router.route('/home', {
  name: 'siteHome',
  onBeforeAction: AccountsTemplates.ensureSignedIn
});

Notice that I make use of an AccountsTemplates.ensureSignedIn function hook to make sure the user is signed in before being able to access the dashboard.

Tweaking the packages

As of 2015-01-31, there seems to be something odd in the useraccounts:core package that prevents the navbar login/logout button from finding the correct sign-in link. As a result, in the at_nav_button.js template helper file, I replaced that particular line with AccountsTemplates.linkClick("signIn");. See example below:

AT.prototype.atNavButtonEvents = {
    'click #at-nav-button': function(event){
        if (Meteor.user())
            AccountsTemplates.logout();
        else
            AccountsTemplates.linkClick("signIn");  // this is correct
    },
};

Another curious thing about the nav button template is that it (by default) comes wrapped in an <li> element. To see what I mean, take a look at at_nav_button.html inside the useraccounts:foundation package. I replaced:

<template name="atNavButton">
  <li class="has-form">
    <a href="#" id="at-nav-button" class="button"></a>
  </li>
</template>

with:

<template name="atNavButton">
  <a href="#" id="at-nav-button"></a>
</template>

This way, I can make sure that it will fit with however I wanted to style the nav bar.

Other settings

There will likely be other settings you’ll want to set. For that, you should create an accounts.js file in the server folder. Then, you can have a look at what the documentation says here about what options you have.

2. Getting email verification up and running

1. Install the email package

This is pretty self-explanatory, but I also recommend that you add the houston admin console so you can easily check to see if your user’s e-mails are verified. This step would just require you to type the following into the command line:

meteor add email houston:admin

2. Acquiring an SMTP Server

For this step, I simply signed up for a free account on mail-gun. There are a lot of options out there, particularly if you don’t mind paying for it. On the free side, there’s mail-gun’s free-level offering which is sufficient for testing at this early stage. Apparently there’s also Gmail’s own SMTP server that you can use for free if you have a Gmail account, but I was not able to get that running. Besides, it was a lot more simple to use mail-gun so I recommend them.

3. Setting up your SMTP variables

Set your environmental variable like so:

// server/smtp.js
Meteor.startup(function () {
  smtp = {
    username: 'username@example.com',
    password: 'p4$$w0rd',
    server:   'smtp.gmail.com',  // eg: mail.gandi.net
    port: 465
  }
  process.env.MAIL_URL = 'smtp://' + encodeURIComponent(smtp.username) + ':' + encodeURIComponent(smtp.password) + '@' + encodeURIComponent(smtp.server) + ':' + smtp.port;
});

If you used mailgun, it should look something like this:

process.env.MAIL_URL = 'smtp://postmaster%40somelongstringofnumbersandletters.mailgun.org:anotherlongstringofnumbersandletters@smtp.mailgun.org:587'

Note that the “%40” in “postmaster%40” represents the @ symbol.

4. Configure routes

And finally, all you have to do is to configure the route for your e-mail verification page:

AccountsTemplates.configureRoute('verifyEmail', {
    name: 'verifyEmail',
    path: '/verify-email'
});

Removing Autopublish

Run command: meteor remove autopublish

This means we will have to write our own rules for publishing and subscribing collections.

// client/main.js
Meteor.subscribe('dailyEntries');
// server/publications.js
Meteor.publish('dailyEntries', function() {
  return DailyEntries.find({userId: this.userId});
});

We make sure to only publish the entries where the userId matches the current user’s userId.

Removing Insecure

Run command: meteor remove insecure

This means that we want to set restrictions on who can insert/update/remove items in a collection. First let’s define a function that we can use to make sure the user owns a particular document in the collection:

// lib/permissions.js
ownsDocument = function(userId, doc) {
  return doc && doc.userId === userId;
}

Now we write allow statements in to let Meteor know under what circumstances the insert/update/remove functions are allowed:

// lib/collections/dailyEntries.js
DailyEntries.allow({
  
  // only allow insertion if you are logged in
  insert: function(userId, doc) { return !! userId;},

  // only allow update/remove if the user owns the document
  update: function(userId, doc) { return ownsDocument(userId, doc); },
  remove: function(userId, doc) { return ownsDocument(userId, doc); },
});
comments powered by Disqus