Usernames? Passwords? So much to remember. Why not let your users login with 1 click using Github? (Or Facebook, or Twitter, or LinkedIn, or Google, or....)
This extra-long EmberScreencasts walks through the implementation of 1-click Github authentication used at EmberSchool.com. It's big, it's complex, but we go through it step by understandable step, and it will end up being great for your users.
Links
Code
$ ember install torii
//config/environment.js
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
torii: {
providers: {
'github-oauth2': {
apiKey: 'YourClientId'
}
}
},
}
}
//services/session.js
import Ember from 'ember';
import ENV from "ember-school-client/config/environment";
export default Ember.Service.extend({
currentUser: null,
torii: Ember.inject.service(),
ajax: Ember.inject.service(),
store: Ember.inject.service(),
login(){
return this.get('torii').open('github').then(data => {
Cookies.set('authenticationToken', data.accessToken)
return this.get('ajax').request(`https://api.github.com/user`)
}).then(user => {
return Ember.$.ajax({
method: "POST",
url: `${ENV.host}/users`,
data: {
login: user.login,
email: user.email,
name: user.name,
authentication_token: Cookies.get('authenticationToken'),
avatar_url: user.avatar_url,
github_id: user.id
}
})
}).then( user =>{
Cookies.set('userId', user.user_id)
this.initializeFromCookie()
});
},
logout(){
this.set("currentUser", null);
Cookies.remove('userId');
Cookies.remove('authenticationToken')
},
init(){
this._super(...arguments);
this.initializeFromCookie();
},
initializeFromCookie(){
var userId = Cookies.get('userId');
if(!!userId){
let user = this.get('store').findRecord('user', userId);
this.set('currentUser', user);
}
},
isLoggedIn: Ember.computed.bool('currentUser')
});
//torii-providers/github.js
import Ember from 'ember';
import GitHubOauth2Provider from 'torii/providers/github-oauth2';
import ENV from "ember-school-client/config/environment";
export default GitHubOauth2Provider.extend({
ajax: Ember.inject.service(),
fetch(data) {
return data;
},
open() {
return this._super().then((toriiData) => {
const authCode = toriiData.authorizationCode;
const serverUrl = `${ENV.host}/github_auth?code=${authCode}`;
return this.get('ajax').request(serverUrl)
.then((data) => {
toriiData.accessToken = data.token;
return toriiData;
});
});
}
});
class SessionsController < ApplicationController
def github
client_id = ENV['GITHUB_CLIENT_ID']
client_secret = ENV['GITHUB_CLIENT_SECRET']
code = params[:code]
@result = HTTParty.post("https://github.com/login/oauth/access_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{code}")
@access_token = @result.parsed_response.split('&')[0].split('=')[1]
render json: {token: @access_token}
end
end
Transcript
Hey everyone. If you’ve been keeping up, you probably know that I’ve launched EmberSchool. And I think it’s really cool and you should go check it out, but the reason I’m bringing it up is because I want to show one of the cool technical features of EmberSchool, that is you can log in with GitHub. So after you’ve approved GitHub, you can do a one click and boom, you’re logged in. And I do that using torii
. Torii is a tool that makes OAuth2 applications easier, so that’s connecting to GitHub, to Twitter, to Google, Facebook. Here’s a list of some of the built-in providers to torii. So OAuth2 is a pretty complex subject. I mean if you go to the GitHub docs on it, they are not exactly easy. It’s not super obvious what exactly you need to do. So what we’ll do is we’ll go through and we’ll do it step by step. And I’m not guaranteeing that the way I’m going to show you is the best way, but it’s a way that works, and it’s specifically about GitHub, but it should help you do all of these other providers as well.
So first, we install the addon. Now we’re installing version 0.8, which as of this recording is the most recent version. Then once it’s installed, we need to set up the environment. So we go to environment.js
and we have a torii
hash and we set up our providers
. We’re going to be using github-oauth2
, and then we’re providing an apiKey
. And don’t freak out that I have this in plain text. This is the client id, not the client secret. So how you’re going to get the client id is... so you’ll go to your ‘Settings’ in GitHub, and then you’ll scroll down to ‘OAuth applications’ under ‘Developer settings’, and you might have to ‘Register a new application’. But I already have mine set up, so if I clicked on it it would give me both this client id and the client secret. I’m not going to click on it because the client secret is actually secret, but the client id you’re going to be sending that with your app anyway, so no reason to hide it.
Now to understand the rest of this video, you’ll need to be familiar with session services and cookies as well, so it’ll be helpful if you watched this older sequence. You could also have watched the sequence covering similar topics in EmberSchool. So we have our session service, and there are lots of places that were calling the session service and then kicking it up there to the login method that we have there. So here we’ve defined login
, and we’re doing quite a bit. We’re going to focus at the start on just this line. So we’re calling to get torii
, and that is of course injected as a service
. Then we’re calling the .open
method. So that’s how you call all of the torii
things. So you call .open
, and here it’s a github
. You could put Facebook or any of the other providers.
And we’re not here using the default GitHub provider. We’re using one that I’ve customized. So what I’ve done is I’ve imported the GitHubOauth2Provider
from torii/providers/github-oauth2
, and I’ve extended it, and I’ve overridden the open
method, but I start off by calling super
. So this isn’t necessarily the recommended way to do torii, but it worked for me because then I could see all the moving parts and make sure that I got the right ones where they needed to be. If you do some of the defaults, there are lots of moving parts and it can be hard to tell what you need to do when. And if you know it really well already, then it can work out really great and be really easy. But if you don’t then it can be super difficult, just because it is sort of a black box. So what we’re going to do is we’re going to put a debugger
statement here and see what kind of data we get from calling super
.
So our first attempt to testing this, hit ‘Authorize’, and of course we will authorize the application, but it just skips through. And this is super weird, I don’t know why it’s doing this, but if we put in a second debugger
, then now it catches it on the second debugger. So please tell me in the comments if you know why it’s doing that. Anyway, so we’ve got the toriiData
and it’s giving us the authorizationCode
and the provider
, the redirectUri
. The important thing is the authorizationCode
, because that’s what we’re going to use next.
So we get that authorizationCode
and we use it to create the serverUrl
. So it’s basically our host name, here www.emberscreencasts.com /github_auth
and then code=
this authCode
. Then we’re going to make an ajax
request there and get back some data
. So we’ll take a look at the server. So on the server, now we have both the client_id
which our client had, and the client_secret
which we did not put on ember because then people could’ve seen it and it’s a secret. And we’re also getting the code
, and then we can send a post
to github
to login
with oauth
and we’re sending the client_id
, the client_secret
, and the code
. And those are all done as query params to this route.
Alright, so we get the result
and then we get the access_token
from here... yeah, there may be better ways, but anyways this does work, and we send back the token
with json
. So we get the data
hash back and we pull the accessToken
from it and we put that on the toriiData
. So the toriiData
now has the authorizationCode
and the accessToken
. And then we send it back to our session service. And that’s what’s in here, in the data route. And we’re going to set the accessToken
as our authenticationToken
, and that’s what we’re going to be using to talk to the server from now on. Now we’re going to pull a lot of user data from GitHub, so we’ll make an ('ajax').request
to the api.github.com/user
link. And you may be wondering, how does it know which user?
So this is the XHR request. Here is the url. And not only does it have the oauth client id, but in the request headers it also has our authentication token. And then you can see in the response preview that it gets all this information or some of its urls where you can get certain information about my id, about my github user, and that user is what’s returned here from the promise. So that’s what’s passed in to the next thenable block, and from there on we go and we create a user on the server, and we’re using all the various fields as well as the authentication from the cookies. And from then on, it’s just the same basic stuff that we covered in this series.
So that’s how we get this cool feature. Very easy login. Let’s go ahead and review. So first, you install torii. Then, you set up your developer application. You might have to register a new one at the place where you’re wanting to connect to, in our case, github. Then you... in your environment.js
, you set it up. Here the apiKey
is called client_id
. Then in your session service, when you’re calling... we call it login
, that’s a pretty good one, then you hit torii
, and you call .open
for the service that you want to use. Here we’re using github
. And then, we’ve also customized it. So the customization is in the torii-providers
folder, in the github
file, and the main thing we’ve done is we’ve extended the GitHubOauth2Provider
which we import from this path, and then we’ve also extended the open
method. So we call super
to get a bunch of data from the default torii call, and then we remove these two debuggers, for some reason we needed two debuggers to stop it instead of one. Anyway, we grab the authorizationCode
and we send that to our server.
So what we’ve done so far is we’ve talked to github and they send us back an authorization code on our user’s client, on our user’s ember app. And then that ember app is sending it to our Rails server, right here. And that Rails server has another secret that it’ll use to send to github again, and it’ll use the client_id
, the client_secret
, and the code
that our ember app has gotten from github. So what’s happened so far is you send it from the ember app to github, and github sends you back a code, and then you send that code to your server, in this case Ruby on Rails, and your server has extra information, as well as that code now, so it’ll call to github again and it’ll get your access_token
. And your access_token
is where so much of the magic happens. Alright, so you get your accessToken
back, and we send it back to the session service, and we set it as the authenticationToken
in our Cookies
. And now that it’s set, we can send it with the request
. So we send a request
to github
in order to get information about the user.
So to go over all this completely, we go from ember to github that sends you back the authorizationCode
. You send the code to your server. The server then combines the code with the client_id
and client_ secret
in order to talk to github to get the accessToken
. Get that accessToken
and then you use that accessToken
in order to request information about that user from github. Now you may be thinking, wow, that sure was a lot just to get this simple feature, and you’d be right, but that is how it’s set up and I think it’s worth it to be able to have something this awesome.