Researching MeteorJS – Authentication

As the 2nd Rate Geniuses told me, authentication with MeteorJS was a piece of cake.

Here’s how it went down.

As you may recall (previous post), we’ve setup a simple multi-page app. It’s got 2 pages. One for viewing and one for administration. The view page should be available with no login. Admin is for logged in users only.

Setting Up a Login System (Authentication)

To setup Meteor auth, I removed the insecure package (turned on by default) and added the accounts packages: accounts-ui-unstyled and accounts-github. This will allow login to happen via Github’s Oauth service.

meteor remove insecure
meteor add accounts-ui-unstyled
meteor add accounts-github

As a first cut, we can add the login form to the index template which will allow signin from anywhere. For my app, the main layout is /app.html. So I started with the following:

<body>
  <div class='login">
    {{loginButtons}}
  </div>
  {{#if currentUser}}
    Welcome, {{currentUser.profile.name}}
  {{/if}}
  <section> 
    {{renderPage}}
  </section>
</body>

This gives you the unstyled (because we’re using accounts-ui-unstyled) login form. From here you’re given a link that tells you as the site-administrator to link in to Github’s Oauth. It involves setting up an API key and secret for the app and a couple of callback URLs. You can follow the instructions from Meteor. Once those keys and callbacks are setup, the {{loginButtons}} partial gives you a ‘Sign in via Github’ link. Easy peasy.

From there, we can start modifying templates to do the right thing based on the state of the current user. If you’re in client/server code, use

if (Meteor.user()) {
 // users only get this
}
else {
 // plain ol' visitors get this
}

and in the templates, there’s a helper attribute/method called currentUser

  <section>
    {{#if currentUser}}
       <div class="for_logged_in_users_only">
       </div>
    {{/if}}
  </section>

One of the downsides (as I see it) is that their built in template for login allows user creation with no email validation (from what i can tell). More importantly, there is no easy way to customize the template to not show the ‘sign up’ link and only have a ‘sign in’ link. For my purposes, I’d love to seed the database with the users that I want, and send them a login invite. Because my “users” are going to be full site editors and should have mostly full reign. And everyone else should not need an account.

To accomplish this, I decided to build a route to a sign in/sign up page which is protected only by obsurity (imagine a login link that is called /you_hit_this_path_to_login). I think it’ll work for now.

Here’s how I setup the routes:

if Meteor.isClient

  Meteor.Router.add
    '/': 'index'
    '/admin': 'admin'
    '/obscure_login_link': 'obscure_login_link'

  Meteor.Router.filters
    'checkLoggedIn': (page) ->
      if Meteor.loggingIn()
        'loading';
      else if Meteor.user()
        page
      else
        'index'

  Meteor.Router.filter 'checkLoggedIn', { except:['obscure_login_link','index'] }

This pushes non-logged in users to the index page and sets up the obscured login path.

Then I moved the {{loginButtons}} bit so it only shows up in the admin and obscure_login_link templates. The only way to create an account is to hit the obscure_login_link which has no external pointers to it, and only allows Github connections, so there are a few hurdles to get over.

Restricted Access based on Login Status (Authorization)

Now, we can assume that there are user accounts in the system. We can use the built-in allow/deny methods in Meteor to restrict data modification.

In our Block model (again, check the previous post to read about the app’s model), we can add the following to models/blocks.coffee

Blocks.allow
  insert: (userId, block) ->
    user = Meteor.user()
    if user
      block.user = user
    user

After this, blocks can only be added by a logged in user. To verify, I ran a couple insert/update requests on the browser console and happily was thwarted by the auth engine.

> Blocks.insert({title:'newblock', color:'olive'})
"f26fa3f8-8400-4ae7-ac94-b6346d6011cf"
insert failed: Access denied

> Blocks.update(Blocks.findOne(), {title:'whatever yo'})
undefined
logging.js:30update failed: Access denied. Can't replace document in restricted collection.

With this small amount of code/config, we’ve now got an app that allows login via Github Oauth and restricts database access to logged in users.

Next steps: getting a test framework setup.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s