Getting Started
Because this recipe calls for a lot of DIY work involving the Meteor authentication system, we’re going to need to add a few packages to our app before we dive in. Let’s take a look at what’s needed and explain what each will do.
Terminal
The accounts-password package is the generic Meteor accounts service. This will allow us to give user’s the option of signing in for Don Carlton Sales using an email address and password.
Terminal
The accounts-facebook package will allow users to connect to and sign in with their Facebook account.
Terminal
The accounts-github package will allow users to connect to and sign in with their GitHub account.
Terminal
The accounts-google package will allow users to connect to and sign in with their Google account.
Terminal
The accounts-twitter package will allow users to connect to and sign in with their Twitter account.
Terminal
The service-configuration package is what we’ll use to configure our own connection to the various third-party services we’ll offer for signing in. This is what allows us to share our OAuth tokens with each which they’ll use to “identify” our application.
Terminal
The http package will give us the ability to call on a third-party API to help us validate new email addresses.
Terminal
The email package will give us the ability to send email from the server using the nifty Email.send method.
Terminal
The ssr package will give us the ability to render HTML templates on the server, which we’ll use to send a “welcome aboard” email to our new users.
A quick note
This recipe relies on several other packages that come as part of Base, the boilerplate kit used here on The Meteor Chef. The packages listed above are merely additions to the packages that are included by default in the kit. Make sure to reference the Packages Included list for Base to ensure you have fulfilled all of the dependencies.
The Sign In Page
To get us started, we need to make sure Don’s team can easily login to the Don Carlton Sales app. In order to do this, we’re going to whip up a simple template that simply asks the user to “sign in” with the service or method of their choice. The pattern, here, is that we’re combining both our Sign Up and Log In process together. Admittedly, we’re going to steal this from Buffer as they’ve “tested” the workflow for us and it seems to work well! Great artists steal…or something. Let’s look at the template:
/client/views/public/index.html
This is all pretty straightforward. The part we want to pay attention to most is the <ul class="btn-list"></ul> element. Here, we give each of our services their own button, rounding out the list with a button to sign in with an email. Next, we’ll look at the controller we’re using to make each of these buttons actually do something. Ignore the {{>signInWithEmailModal}} inclusion, we’ll tackle that in a bit.
A quick note
In this recipe, we’re only focusing on the more popular OAuth implementation that Meteor offers. Along with Facebook, GitHub, Google, and Twitter, Meteor also offers access to OAuth login for Meetup, Weibo, and the recently released Meteor Developer Account service. Like we’ll discover with Twitter, Weibo and Meteor Developer accounts also do not offer the ability to request specific permissions for users.
Wiring Up Sign In’s
Our controller for handling Sign In’s is mostly simplistic, too. What’s nice is that Meteor is a peach when it comes to handling third-party logins.
/client/controllers/public/index.coffee
Nice, right? All of our events look fairly similar here. What we’re doing is looking for a click event on each of our buttons and calling to the login service associated with it. The big thing to pay attention to is that, despite all being realtively similar, Meteor uses a convention of specifying the name of the service Meteor.loginWithService.
But what about Twitter? Ah yes, our dear friend Twitter. As we’ll continue to learn throughout this recipe, Twitter doesn’t exactly play friendly with our game plan. Maybe not so dramatic, but notice that we’re missing the requestPermissions: ['email'] part for Twitter. This is because their OAuth implementation doesn’t offer up email addresses, or as we learned earlier, the ability to request any permissions. Wonky. Don’t worry, we’ll cover how to handle that so you’re not caught off guard in a bit.
There’s one more step for setting up our third-party logins which we’ll cover later in the recipe. For now, let’s take a look at how we’ll handle a sign in with email. This is a bit more tricky as we’ll need to handle both log in and sign up at the same time.
Sign In With Email
As we’re stealing our sign in pattern from Buffer, we’re also going to make use of their convention of a modal overlay for signing up and logging users in. Recall back to our index.html template:
/client/views/public/index.html
Here, we’re calling to another template signInWithEmailModal where we’ve stored the actual contents of our modal. We do this here because we want our modal available in our index template. Wait a sec…when we went over the events earlier we didn’t have an event for showing this modal. What gives?
/client/views/public/index.html
In order to fire our modal, we’re making use of Bootstrap’s data-toggle and data-target attributes which help to automate the reveal of our modal. Note: this is hyper specific to this implementation. If you won’t be using Bootstrap, you (likely) won’t be doing this.
Okay, so let’s take a look at our modal. Again, we’re leaning pretty heavily on Bootstrap markup-wise for this, so keep that in mind if you’re implementing something different. Let’s take a look at the <form> portion of our modal (where we’ll actually be handling user input):
/client/views/public/sign-in-with-email-modal.html
Real simple. We’re asking for an email and a password, that’s it. But, we’re offering up two submit buttons (whaaaat). Here, we present the user with a create-account button and a sign-in button. Let’s take a peek at how we make this work.
/client/controllers/public/sign-in-with-email-modal.coffee
There’s a lot going on in this file, but let’s start with our events. Everything here is realtively straightforward. Starting at the bottom, notice that our submit form event is just looking to prevent the form from submitting on its own. If you read recipe #2 this should look familiar.
This is where we peacock. On our click .btn-create-account and click .btn-sign-in events, we’re setting a Session variable createOrSignIn. What’s cool about this is that we’re able to tell our app which button is being clicked, meaning, both buttons submit the form but, we can use our Session variable to communicate how we want the form submitted (whaaaat).
A quick note
We’re at two mind blows now. Hope you’re hanging in there, sport.
This may not make total sense, so let’s jump up to our submitHandler function that’s a part of our validation step.
/client/controllers/public/sign-in-with-email-modal.coffee
First up, we find that we’re assigning our dual-button session variable whatchamacalit to a local variable called createOrSignIn. Next, after assigning the value of our email and password inputs to an object, we test the value of our createOrSignIn variable to see where we should send the user next. If our variable equals create (set by our click on the “Create Account” button in our modal), we set the user up with a new account. If the user has clicked “Sign In” instead, we simply log them in. Sweet!
But wait what’s this call to a validateEmailAddress method? Well, kid, I need to tell you a little story. It’s about a little thing called spam and the evil people that use it. You see, evil people like to do things like make fake accounts, sign up for services with dummy emails, and all sorts of other not-so-fun stuff.
Some of these people have valid reasons, but most of them are just raining on our parade. This method is allowing us to make sure that, without a doubt, our user is signing up with a 110% legit email address. Let’s hop over to the server quick to see how it works.
/server/email/validation.coffee
What the heck is all of this? Well, because email validation is a pain in the butt and for the sake of time, here we’re making use of a third-party email validation service. Kickbox, which was recommended by the folks at Mailgun, is an API service that allows you to test email addresses for their existence.
So how exactly are we using it here? First, you’ll notice that we’re creating a variable called Future and doing an Npm.require to ('fibers/future'). This is giving us access to the Future’s portion of the Fibers NPM package which we’ll use to handle the flow of our HTTP method.
What’s unique about this is that if you’ve ever added an NPM package to your app before, you'l notice that we didn’t use a package like meteorhacks:npm or create our own local package to import from NPM. What gives?
Because Meteor is itself Node-based and they make use of the futures library in Meteor’s core code, it’s technically already loaded into our application. Here, a simple require lets our app know that we’d like to make use of it. Nifty!
So, why do we need this? Our next step (after using check() like upstanding citizens) is to make use of the http package we installed earlier. Here, we call on the Kickbox API (specifically their /verify method), passing our email address and super secret API key. Note: you’ll need to sign up for Kickbox and generate your own API key to get this working. This step isn’t required, but highly recommended for keeping your user list clean.
Futures comes into play because all HTTP.call functions are run asynchronously. This means that the code runs and Meteor keeps on truckin' instead of waiting for it to finish. What we’re really looking for here is for Meteor to hit this function and wait until it’s finished.
We want to wait because the answer we get back from Kickbox will determine whether we allow our user to sign in, or kick em' to the curb. Okay, maybe not that harsh, but it will allow us to notify the user if they’re trying to sign up with a bum email.
/server/email/validation.coffee
A few things to pay attention to. The first is actually the last. Where in a normal function we’d just return some value, here, we’re returning our Future validateEmail with a .wait() method invoked on it validateEmail.wait(). What this is doing is telling the Future’s library to pause the running of the script until it receives a value. When it does, it continues running returning whatever value it was passed.
Up a little bit into our code, we can see that we’re making use of our Future’s .return() method to pass it some data based on the outcome of our HTTP request. We test for two instances (three, technicaly): first, if the HTTP request throws an error (e.g. a bad URL, no response from the API, etc.) we want to grab that and return it.
Next, if the request does go through and we get a response from the server, we test to see whether the value of the response.data.result key is either invalid or unknown. These keys/values are specific to Kickbox and tell us whether the email we sent them is legitimate. Here, we test for a falsey value first returning an error if the email is bad. If not, we simply return a boolean true value.
/client/controllers/public/sign-in-with-email-modal.coffee
Back on the client and inside of our Meteor.call 'validateEmailAddress' function, we watch on the error and response arguments. Here, if we get an error (e.g. from the API) we alert it to the user. We do the same if our response was set to an error (i.e. the one we defined, “Sorry, your email…”). Finally, if no errors are present, we assume the email is valid and create the user’s account.
Awesome! With this in place we’ve actually completed getting user’s signed in with email. Next, we need to revisit our third-party sign in’s and get them configured so they will actually work.
Configuring Third-Party Services
Because our third-party sign in’s are relying on external services outside of our control, we need a way to identify our application with those services so they know their users are safe. Fortunately for us, some smarter folks in the past came up with a convenient system known as OAuth, or, “open authentication”:
OAuth is an open standard to authorization. OAuth provides client applications a ‘secure delegated access’ to server resources on behalf of a resource owner. It specifies a process for resource owners to authorize third-party access to their server resources without sharing their credentials.
— via “OAuth” on Wikipedia
What this essentially means is that by providing a service with a unique token for our application, we can make requests for information on behalf of the user. So for things like signing in, we can allow the user to use their email/password combination from another service (e.g. Facebook). We never store or touch that email/password, because OAuth implements a permissions system wherein users are prompted to log in to and accept or deny our access to their credentials. Pretty cool, right?
So what we need to accomplish now is the “providing a service with a unique token” part. This is done by making use of the service-configuration package we installed earlier. By adding this, we gain access to a set of functions that allow us to update Service Configurations in the database: ServiceConfiguration.configurations.remove() and ServiceConfiguration.configurations.insert().
Together, these two allow us to set our OAuth clientId and secret in the database. Calling back to our client code, these values are referenced by Meteor when we call any of the Meteor.loginWith<Service> functions. Let’s see how we get them setup.
/server/admin/startup.coffee
To keep our code DRY, we’ve setup a function createServiceConfiguration() that will wrap the two ServiceConfiguration functions (via Meteor) above. We’re doing this because for each service we want to support, we’d need to run both of these functions. Putting them into a single function and simply passing over the parameters they need access to saves us a few lines of code. Nice!
Inside of our function, first per Meteor’s documentation, we run our ServiceConfiguration.configurations.remove() function to “reset” any existing configurations in our app. Because this will all run on startup, we want to ensure that we’re clearing out any old configurations. This is nice for when you’re running a production application and reset your API keys. Having this ensures that when you update those keys in your code, they actually “stick.”
Next, we present a strange combo: an object labeled config and then a switch/case statement that references the config object. What is this?
By default, OAuth applications are required to provide two keys to developers: clientId and a secret key. The clientId acts as the identifier for your specific application, whereas the secret acts like your password (you as the developer, not your user). When we call on an OAuth service, we pass these keys to identify ourselves.
What you’ll notice above is that we have three “configurations”: generic, facebook, and twitter. If you have a keen eye, you’ll notice that the only difference between the three is the clientId field. This setup accounts for Facebook and Twitter’s variation on the naming of these keys.
Here, we use our switch/case statement to look at the name of the service passed as an argument to our function. Depending on what is passed, we then run ServiceConfiguration.configurations.insert(config.service) function, passing the “configuration” from above. What this achieves is having the correct key/value pairs and names in place so that when Meteor calls on a given service, what it sends over (again, the clientId and secret) match the naming conventions of that service. Said another way: you say tomayto, I say tomahto.
Okay, so we’ve got this all setup, but where and how do we call it? Just beneath our function declaration you’ll find four calls to our createServiceConfiguration() function:
/server/admin/startup.coffee
Just like we setup our function to expect, we pass three arguments: service, clientId, and secret. Isn’t this nice? Instead of having the same code copied over and over, we get a nice one-liner for configuring each service. But…where do we get these clientId and secret keys? Good question!
Each service has their own system for registering your application and generating keys. Admittedly, some are really easy and others are a bit confusing. To get you on the right track, depending on the services you’d like to support you’ll need to visit the following links:
Facebook Developers
GitHub Applications
Google Developers Console
Twitter Apps
At each site you’ll need to do two things:
Register your application and obtain a clientId and secret.
Set the correct URLs for that service.
While all of these processes are fairly similar, we should call attention to a few things that can be confusing. Let’s take a look at some of the pitfalls we might run into while getting this all set up.
Configuring Facebook
When it comes to Facebook, getting our appId and secret is realtively straightforward, but we need to make sure we get the App Domain configured correctly. The App Domain is the URL that Facebook expects requests to be coming from in association with the appId and secret you’ve specified in your code. If the values set in your application code are sent from a domain that does not match the values in the dashboard on Facebook, you’ll get an error.
To get this working on Facebook, once you’ve setup your application, head over to the dashboard your your application https://developers.facebook.com/apps/<App ID>/dashboard/ and click on the “Settings” tab on the left. From here, you’ll need to click the “Add Platform” button (selecting Website), and specify your “Site URL.”
The value of this needs to be the full URL of your application, e.g. http://localhost:3000. Once you’ve set this up, you’ll need to go up to the App Domain field and specify just the domain of the application, e.g. localhost. These two values need to match the exact URL of your environment. For Facebook, you can only specify one Site URL per application, meaning when you want to go into production, you’ll need to update the Site URL and App Domain values.
Alternatively, you could also setup a separate application for local development and another for production. Just make sure to keep track of your clientId and secret keys for each of your configurations.
Configuring GitHub
GitHub makes things a little bit easier, though, we still want to pay attention to the Callback URL we’re setting. Just like Facebook, this URL needs to match the current domain of your application. If we’re on http://localhost:3000 it should be that, if we’re on http://doncarltonsales.com it should be that. Without it, you’ll get a mismatched URI error and get a one-way ticket to frown town.
Configuring Google
Despite having a slightly confusing interface for managing your applications, Google isn’t too bad to get setup. Once you’ve created your application, you’ll want to access the Credentials menu item underneath the APIs & auth heading, clicking the “Create new Client ID” button.
On the resulting popup, you’ll want to select Web Application for Application Type and then fill out the subsequent information for the “consent screen” (this is the popup your users will be greeted with asking for permission to access their account). Once you’ve filled this out, you’ll be greeted with a popup to setup two things: “Authorized JavaScript Origins” (spooky!) and “Authorized Redirect URIs.”
The first, origins, is simply the URLs where Google should expect requests to come from. Unlike our previous services, they actuall do let you specify multiple URLs (one per line). Here, you’ll want to place your localhost URL and your production URL.
In the next box, Redirect URIs, you’ll want to place the URLs that Google will send the user back to after they’ve been authenticated. You’ll notice that in our example image, we’re appending a funky string to the end of the two URLs we set for localhost and production, what gives?
Well, although it’s not documented, Meteor automatically sends Google OAuth requests back to the root domain of your application, appending _oauth/google?close onto the end of it. Cute! In order to compensate for this, we need to make sure that we update both of the URLs we specified for our JavaScript Origins to include this, so:
Once those are set, click “Create Client ID” and you should get your clientId and secret! Wonderful.
Configuring Twitter
So, Twitter. We’ll give them a hard time but they’re actually quite pleasant to set up. Fortunately, their OAuth application registration is quick and painless, we just want to call attention to two things. Both pertain to how we setup our Callback URL. First, if you’re looking to test your application on localhost, Twitter won’t let you! Ha! Where the other services would let us set our Callback URL as http://localhost:3000, Twitter is a straight up thug and says “no.”
Instead when we’re working on localhost we need to use the less familiar but equivalent http://127.0.0.1:3000. If this is gibberish to you, 127.0.0.1 is the default local IP address of your computer, which is the same as http://localhost:3000. localhost is merely a convenient shorthand (like a domain name on a website). Great, so this is set but we’re not done just yet!
Remember that wack-a-doodle string Google needed at the end of our Callback URL? Twitter needs it, too! This time around it looks like this: http://127.0.0.1:3000/_oauth/twitter?close. Not terrible. Of course, the same rules apply here: if you’re moving into production, you’ll need to swap 127.0.0.1:3000 with your production domain, or, setup a separate OAuth application specifically for production.
A quick note
Holy cow! We’re in the home stretch. How about we take a break for some calisthenics?
Sending Welcome Email
The second to last thing we need to do is welcome our new user to our service! This is optional,
but it’s a good opportunity to “onboard” the user and confirm their account creation. In order to send off our email, we’re going to make use of a handy function given to us by Meteor Accounts.onCreateUser(). Just like it sounds, when a new user is created by Meteor, this function is called. Let’s hop over to the server where this is running in our code:
/server/admin/account-creation.coffee
We have a few things going on in here. First, you’ll notice that this function gives us two arguments to make use of: options and user. options are parameters set in a call to Accounts.createUser, or, a call to a third-party login service (e.g. username/email, password, profile, etc.). user is the proposed user document that Meteor will insert into the database. Pay attention to that. This function is technically being fired before Meteor inserts the new user. What does this mean?
The function should return the user document (either the one passed in or a newly-created object) with whatever modifications are desired. The returned document is inserted directly into the Meteor.users collection.
— via Meteor Documentation
This means that Meteor looks at the return value of this function for what to insert as the user in the database. We absolutely must return the user document at the end of our onCreateUser() function. Also explained in the docs is that this function overrides the default hook Meteor would use to perform this process, meaning, we have to account for any default behavior in our own function (e.g. adding the user’s profile to their user object).
You can see in our code above that we do want to preserve the user’s profile information, so we make sure to test for it and set it on the user document if it exists. Great!
Let’s jump back up to the top of our Accounts.onCreateUser() function. We’re creating an object called userData and setting two keys email and name to some funky values. What’s going on here?
First, in our email key, we’re calling a function we’ve defined called determineEmail() and passing our user argument given to us by Meteor.
/server/admin/account-creation.coffee
This function is designed to help us account for the fact that Meteor does not store user email’s the same for each of its different authentication methods. Instead, it uses two conventions: when a user is created via accounts-password, the user’s email is set in the user.emails array in the database. When using a third-party OAuth service, Meteor is storing the user’s email in a services object and a nested service-name (e.g. facebook) object. Woah!
To compensate for this, our determineEmail() function looks to see what method of storage is being used for the user’s email and then returns the value that it finds. The part to pay attention to is the case/switch function being used to check third-party services. This is saying if the services object exists on the user, check for each of our known services and when you get a match, return the email field for that service.
You’ll notice that our dear friend Twitter is returning null instead of an email. Well, sorry to be the bearer of bad news, but Twitter’s OAuth API does not offer an email address. This isn’t just for Meteor, this is for anyone using their OAuth API. Not cool, Twitter. Not cool. By returning null we can later test for an email value to protect ourselves from attempting to send to an email that doesn’t exist. We can see this test in action back in our Accounts.onCreateUser() function:
/server/admin/account-creation.coffee
Recall that userData.email is the result of our determineEmail() function. If the value isn’t null, we call to a Meteor method sendWelcomeEmail to do our bidding! Nice.
Now, the last step of this is to actually send an email. How do we do it?
/server/admin/account-creation.coffee
Because we’d like to send a spiffy HTML email to our new user and we’d like to personalize it,
we need a way to render our email template on the server. Enter the meteorhacks:ssr package. Just like it sounds, this package gives us the ability to do exactly what we want. In order to do it, it gives us two functions to work with: SSR.compileTemplate() and SSR.render().
The first, compileTemplate() is where we define the name of our template 'welcomeEmail' and pass a call to Assets.getText() with the path of our email template. What’s this about? Recall from Recipe #1 that this function simply pulls in the text of a file we specify. The path it looks at is relative to the /private directory in our project root. So here, we’re importing our email from /private/email/welcome-email.html.
Once we have our template defined and available as 'welcomeEmail' for ssr to use, we call the render() method, again passing the name of our template 'welcomeEmail' along with the values we’d like to make available as template variables (e.g. {{name}}) in our email. Here we pass two values: name and url. The first, name, is our attempt to personalize the user’s email. If we open up our email template, we can see how this is used:
/private/email/welcome-email.html
Simple, but a nice touch. Because we’re using good ol' fashioned Handlebars templates, we can make use of the handy {{if}} statement to check if name is set. So cool! Further down in the email, we can also find our use of the url key set above:
/private/email/welcome-email.html
Here we use our URL value to link the user back to our application.
The last part of our code is focused on sending our email via the Email.send() method which was given to us when we installed the email package earlier in the recipe. We quickly set some obvious parameters: to, from, and subject, finishing with the key html which is set to the emailTemplate variable containing the result of our call to SSR.render(), or, our template updated with the values we passed to it!
A quick note
We’re going to skip over configuring our email address in Meteor to save time. Recall that in order to do this you need to make use of the MAIL_URL environment variable in your server code. You can learn more about environment variables here. Also, check out Recipe #2 where we go into detail on setting this up.
Displaying Our User’s Email on Template
Good, good, good. We’re onto our last step which is something simple but important: displaying the user’s email on the template. Once our user’s are logged in, we want to be able to get display their email in our dropdown menu where they can “logout.”
To accomplish this, we need to reprise a bit of our code from earlier, the determineEmail() function. This time, however, we’re going to wrap it in a UI helper so we can make use of it in our template. Let’s take a look:
/client/helpers/helpers-ui.coffee
Almost identical to what we did earlier (so much so that you can score easy refactor points in your own app by making this a global function). There are two big differences here: first, we don’t have access to the user document like we did earlier, so we need to take a passed userId argument and look up the user in the database. Our other difference is for Twitter. Recall that they do not give us an email to work with, so, instead we opt for the user’s screenName or @name (e.g. @themeteorchef).
To make use of the helper over in our template code, we can now call {{userIdentity}} passing the current user’s ID as a parameter:
/client/includes/_header.html
Here we do a bit of piggybacking on Meteor’s {{currentUser}} template variable, looking up the _id value to send back to our helper. We check for existence of the value for good measure and if it exists, output it to the template.
We’re all don…no we’re not!
This is super important. Because we’re interacting with our user’s data, we need to be careful about what data is getting to the client. To control this, we’ve setup a publication on the server to specify exactly what we need, along with a subscription in our /dashboard view.
/server/publications.coffee
We’ve kept things a bit verbose here so we can see what’s happening. First, we set a variable currentUser equal to this.userId which is a convenient value set for us by Meteor so we don’t have to pass our user’s ID to our publication.
Next, we test for the existence of that value and if it’s available, we publish the data for our current user and specifically request the fields that we want. Notice that because we only need access to their profile and email address, we’re only requesting those fields. This is important because if we didn’t do this, we’d be sending the user’s entire document to the client. This is a big no no.
Phew. Alright, so we’re safe there. The very last thing we need to do is ensure that we can actually see the data we’re publishing. In our /dashboard route definition:
/client/routes/routes-authenticated.coffee
We make a call in our waitOn function to subscribe to our userData publication on the server. That’s it! Now our user’s data will be accessible on our /dashboard route and we can display their name. Awesome!
We’re all done! Now we can kick back and enjoy that we have a custom authentication setup complete with support for third-party services. Yeah, go ahead and peacock, you deserve it.
Wrap Up & Summary
In this recipe we learned how to create our own authentication setup complete with support for email and password users, as well as user’s logging in with third-party accounts. We learned about validating emails for authenticity, configuring third-party networks, and even how to send a welcome email to new users! Lastly, we learned about the importance of publishing only the data we need and creating a UI helper to help us display that data in a template.