Authentication with Torii
So, you’ve built an Ember app and you’re looking to setup authentication. You decide to integrate with Facebook because of its widespread usage. At first the integration works fine, but then you find out a large number of your users have long standing gang related beefs with Mark Zuckerberg. Now you need to support multiple types of authentication. No problem, Torii offers a great solution.
Torii allows us to create common interfaces for authenticating against different services, and thus use many services — e.g. authenticating with both Facebook and Gmail.
Torii isn’t concerned with how you decide to authenticate, it’s just a of set conventions for authentication. In this tutorial we’ll look at an OAuth2 work flow, because it is common and it will illustrate the Torii library in its entirety.
Throughout this tutorial I will try to keep conceptually distinct the details of OAuth2 from the design of Torii. Once you understand Torii is an abstraction around authenticating and is agnostic to any one authentication method, the library is much easier to understand.
The Basics
Torii consists of three basic components:
- The Provider: The provider is responsible for authenticating against a specific platform e.g. Github or Twitter. In this tutorial the provider will handle receiving an Authorization Grant from a fake OAuth2 service, but it could also be used with any third party service which provides authentication tools.
- The Adapter: The adapter is a series of methods built to change authentication state. It provides your session with core methods to retrieve and destroy secured data.
- The Session Manager: The session manager is a tool highly integrated with your adapter. It is a layer that should be isolated from the logic behind your authentication. It provides a simple interface for access to protected data.
The Provider
The provider is responsible for setting up communication between your client app and a third party authentication service. Torii ships with many built in providers so I would recommend checking out the addon to see if there is already a provider for the service you are using, there probably is. If not, you’ll need to create your own.
The bare minimum a provider must do is define an open function. The return value of the open function will be passed as an argument for the open function on your adapter. Torii expects open to return a promise which, if resolved, will authenticate the user and, if rejected, will fail to authenticate. Unlike me, who if rejected will disappear into a room for a week to binge watch rom-coms.
Our Provider:
Our Adapter:
The body of the promise on the Provider’s open function could be an AJAX call to a third party server and if the server returns a value of “yer safe dawg”, then you resolve the promise. Perhaps it is a AJAX request to a server you own and you resolve the promise as long as the request doesn’t 403. The point is, if you make a provider, you have to determine what conditions are necessary to resolve or reject the promise. You have to determine what constitutes failing or passing authentication. We are using OAuth2 so we want to resolve our promise only if we receive an Authorization Grant.
If you want to roll your own integration with a service or you aren’t using OAuth2 you can stop and skip down to the adapter section of this post like the eager learner you are.
Torii, however, does have an OAuth2 blueprint. To use their OAuth2 blueprint you simply subclass their OAuth2 code and define four properties. Additionally, we import configurable which will give us access to API keys define in environment.js.
Sweet, so assuming there existed an ourOAuth2Provider.com service we would now be done with our provider. We can actually use this provider in isolation. For us, this is a somewhat contrived example because we can’t do much with only an authorization grant. If you were using a different service this provider step may actually provide meaningful information about the user, like a user’s name or a user’s favorite episode of Keeping Up with the Kardashians.
Not all OAuth2 services are created equally and different authentication providers do different things, so make sure you understand the workflow you’re implementing before you try to implement it. I recommend playing around until you can successfully retrieve an Authorization Grant or equivalent protected data, before trying to use Torii’s built in template.
The Adapter
The adapter layer is responsible for defining three main methods for your session. Each of these methods will probably be different for each adapter. Each method represents a different manipulation of the authentication state and should return a promise. A resolved promise will indicate that the method has succeeded. A rejected promise will indicate that something has gone wrong.
- open: Open is responsible for authenticating and recovering user information that you desire. This could be as simple as an access token, or it could be a large object full of a user’s personal data. This often is logging in.
- fetch: Fetch is responsible for determining if a user is authenticated, which often means checking if a user is already logged in.
- close: Close is responsible for clearing out the session and ending the authentication; logging out.
So now that we have defined what we want these methods to do, lets take a look at implementing them. First lets look at the open method, or logging a user in.
Open
Here we are ‘open’ing a new authenticated session. Remember, this can be totally different depending on how you are authenticating. We are completing a simple OAuth2 workflow, so, we make a asynchronous request to our server with our Authorization Grant we obtained with our provider. Our server then uses our authorization grant to obtain an access token and protected information about the user. This process can be different depending on the OAuth2 service. If this part is unclear, I recommend reading the OAuth2 spec or giving up, you can always give up.
Now, with our obtained data we either resolve or reject our promise. The code above checks whether the response has a user token, but a more likely scenario is to make sure our response is the appropriate HTTP status code. If we have the information we want, we resolve the promise with the data. The data will then be merged into the session. If we reject the promise our session will know that the authentication failed.
Note we are storing the user access token in local storage. Why we do this will become clear once we implement fetch.
Fetch
For a ‘fetch’ we are trying to determine if the user is logged in. If localStorage has our token then we’ll say the user is logged in, because we have our access token which is used to identify a single user.
At any time we could use our access token to make a call back to the server and store or retrieve information associated with our user. For example, to store a comment, we would send the body of the comment back to our server with the access token. Then using the access token we retrieve the user’s email from our third party and associate the comment with the user who’s email we just retrieved.
Close
The close method is tasked with destroying our authenticated session. The fetch method determines whether or not a session is authenticated based on an access token being defined in localStorage. So, it follows, that in order to make our app realize things are not authenticated we should remove the thing we have defined in localStorage. Unless the thing stored in localStorage is a unique, culturally relevant, work of art, in which case we should save it and cherish it forever.
If this all feels overwhelming, that’s alright. The session manager is where everything comes together.
The Session Manager
The session manager is a simple interface for using your provider and adapter. The session manager and the adapter are strongly coupled. If you choose to use the session manager you will also be using the adapter and vice versa.
To start using the session manager we must configure it in our Ember environment.
We can name our session whatever we want with the sessionServiceName property but we will keep it simple and name it “session”. Now in every route and controller we can access the session using:
this.get(‘session’).fetch(‘some-oauth2’)
The session is tied directly to the adapter’s three main methods. Our discussion of the session will mostly revolve around how to use these three methods effectively.
The easiest way to use these methods is in your application route. We want to check if the user is authenticated. So in our application route we should try to fetch our session before we do anything else. Using Ember’s beforeModel hook we can fetch our session. Then if we want to logout or signIn, we need to correspondingly call close or open on our session. So we need actions which can be used to call those functions.
Our session maintains some very basic state:
session.isAuthenticated
will tell us whether or not our our session successfully authenticated. So now we have a property for conditionally showing information based on whether or not a user is authenticated. So a navigation template might look something like this.
The isAuthenticated flag is not the only piece of information the session contains, logging the session to the console will show you other useful information the session maintains. Things like is ‘isWorking’ which will be true while waiting for your promises to resolve or ‘isProud’ which just always returns false.
Naming Conventions
Torii has naming conventions. Make sure you are naming things consistently. If you name your provider:
torii-providers/bradley.js
Then you must also name your adapter:
torii-adapters/bradley.js
Otherwise it will look for behavior on a global application definition:
torii-adapters/application.js
Alright, now you’re all set to integrate multiple authentication providers into your app! Checkout the addon for more details about the Torii library.