Implementing Oauth and Meteor for the Glitch API

Here’s how I got oauth working with meteor and the Glitch API. If you just want to see the working code, here’s a gist with the important bits. Read on for the full story.

Theory

The first hurdle was understanding the oauth authentication types. The Glitch API described two alternatives: code and token. They also used the terms server-side and client-side. While it all makes sense to me now that I’ve implemented it, it was all very confusing at first.

The end goal is to get an oauth or access token that you can use to make authenticated calls against the API. In order to get this token granted to you, the end-user needs to authorize your application with the oauth provider. There are two options with the Glitch API (I imagine these are common to most Oauth2 implementations).

Option 1: Client-side or 1-step or token

Send the user to the authorize url (https://api.glitch.com/oauth2/authorize) with response-type “token”. You’ll also include your applications client_id. The response back from the provider will have the auth_token that you can use to make calls. Once you have the auth_token, you can make the authenticated calls from the client or the server. The “client-side” refers to the fact that the token is returned to the client.

The downside of this method is that the client can see the auth token, which is generally considered a bad idea. If someone figures out the auth token, they can make calls on your behalf. This is because once the authorizations is done, you don’t use your client_id or client_secret anymore, the only check is against the auth_token. For this reason, it’s recommended to use the other method.

Option 2: Server-side or 2-step or code

In this case, send the user to the authorize url (https://api.glitch.com/oauth2/authorize) with response-type “code”. The provider will return an authorization code when it redirects, and this is all the client sees. By itself, the code isn’t worth anything. On the server-side, you combine the code with your client_id and client_secret and make a request against the token url (https://api.glitch.com/oauth2/token). If the code is proper, then the provider will return the token to you.

Now for the implementation details

First step is sending the user to the authorization endpoint. I crafted a url with the necessary parameters and dropped this into my view as a login link.

https://api.glitch.com/oauth2/authorize?response_type=code&client_id=000yourclientid000&redirect_uri=http://yoursitehere/oauth/&scope=identity

When the user redirects back, the url has the auth code embedded as a parameter. In most other languages/frameworks, you could pull this code down on the server to proceed. However, meteor doesn’t currently support server-side routing and my meteor-fu was not strong enough to figure out a way to do this on the server. I got around this using backbone and capturing the code on the client side.

var Auth_Router = Backbone.Router.extend({
	routes: {
		"": "root",
		"oauth/?code=:code":	"auth"
	},
	root: function () {},
	auth: function (code)	{
		Meteor.call('authenticate', code, function (error, result) {
			//do something with returned data here
		});
		this.navigate('');
	}
});


Meteor.startup(function () {
	var router = new Auth_Router();

	Backbone.history.start({pushState: true});
});

With this code, as soon as the redirect occurs, the backbone router will grab the code and perform a Meteor.methods call against the server. There is also a navigate call to clear the code from the url.

Now on the server side:

Meteor.methods({
	authenticate: function (auth_code) {
		this.unblock();

		var res = Meteor.http.call("POST", "https://api.glitch.com/oauth2/token",
			{params: {
				grant_type : 'authorization_code',
				code: auth_code,
				client_id : secrets.glitch.client_id,
				client_secret : secrets.glitch.client_secret,
				redirect_uri : secrets.glitch.redirect_uri
			}});

		if (res.statusCode === 200) {
      //use the access_token to retrieve something useful, for example
			return auth_check(res.data.access_token);
		}
	}
});

var auth_check = function (token) {
	var res = Meteor.http.call("GET", "http://api.glitch.com/simple/auth.check",
		{params: {
			oauth_token : token
		}});

	if (res.statusCode === 200) {
		var json = JSON.parse(res.content);
                //do something with the content here
	}
};

And that’s all there is to it. Well, that would be if everything worked as intended. At this point, I still wasn’t getting the token back, so I kept digging.

Troubleshooting

After getting the http call setup, and verifying I was receiving a code back from ‘authorize’, I started to see this error every time I sent a POST against ‘token’, even though I was sure I was sending a grant_type of ‘authorization_code.’

{ statusCode: 400,
  content: '{"error":"unsupported_grant_type","error_description":"Only the \'authorization_code\' grant type is supported at this time."}',
  headers:
   { date: 'Thu, 03 May 2012 23:18:40 GMT',
     server: 'Apache/2.2.17',
     'cache-control': 'no-store',
     'content-length': '124',
     connection: 'close',
     'content-type': 'application/json' },
  data:
   { error: 'unsupported_grant_type',
     error_description: 'Only the \'authorization_code\' grant type is supported at this time.' },
  error: [Error: failed] }

It turns out that the provider is complaining about the grant_type because it can’t parse the parameters from the POST body properly. Although the meteor docs say that the POST is being encoded as x-www-form-urlencoded, I had to explicitly specify a the content-type header in my http function call. Here is the full code for the function:

var res = Meteor.http.call("POST", "https://api.glitch.com/oauth2/token",
	{params: {
		grant_type : 'authorization_code',
		code: auth_code,
		client_id : secrets.glitch.client_id,
		client_secret : secrets.glitch.client_secret,
		redirect_uri : secrets.glitch.redirect_uri
	}, headers: {
		"content-type": "application/x-www-form-urlencoded"
}});

My next attempt returned this error, but only because I was testing with a stale auth code (this is before I got the backbone routing working).

{ statusCode: 400,
  content: '{"error":"invalid_grant","error_description":"The supplied \'code\' was not recognized."}',
  headers:
   { date: 'Thu, 03 May 2012 23:26:11 GMT',
     server: 'Apache/2.2.17',
     'cache-control': 'no-store',
     'content-length': '87',
     connection: 'close',
     'content-type': 'application/json' },
  data:
   { error: 'invalid_grant',
     error_description: 'The supplied \'code\' was not recognized.' },
  error: [Error: failed] }

After grabbing a fresh auth code, I was able to procure a valid access_token.

{ statusCode: 200,
  content: '{"access_token":"[redacted]","token_type":"bearer","scope":"identity"}',
  headers:
   { date: 'Thu, 03 May 2012 23:26:51 GMT',
     server: 'Apache/2.2.17',
     'cache-control': 'no-store',
     'content-length': '122',
     'keep-alive': 'timeout=5, max=100',
     connection: 'Keep-Alive',
     'content-type': 'application/json' },
  data:
   { access_token: '[redacted]',
     token_type: 'bearer',
     scope: 'identity' },
  error: null }

Next step: figuring out some session persistence with meteor so the user doesn’t have to reauthorize after every page load.

Back