Combining client-side social login and server-side authorization with Cordova and Node

This post is more than 2 years old.

I believe this wins the title for the longest blog title ever. So what in the hell am I talking about? It isn't too difficult to add a social login aspect to your Apache Cordova application. I've used a variety of plugins/libraries in the past and for the most part - it just plain works. Recently however I ran into an issue that I didn't know how to get around. Given that your mobile client authenticates a user against some OAuth provider, and given than you then allow that user to interact with your server, how do a) ensure that the person running the server API is authenticated and b) how do you uniquely identify that user? That's a bit abstract, so how about a real example?

Imagine you are building a mobile app that lets a user write notes. While you could store the notes on the client, you really need to store them in your own back end storage. In order to ensure that only authenticated users access your server APIs, you could build your own user system, have the user login (over https of course), store a session variable with their account info, and when they create data (or attempt to read data), use a primary key of some sort to get the right data.

That's fairly boilerplate. But what happens when you mix in social login on the client-side? In that case, the mobile app handles the OAuth request to the provider and the provider returns a token. So on the client-side you know the current user is kosher. But when she makes a request to your Node (or ColdFusion, PHP, etc) server, how do you handle knowing the user was logged in - and even better - how do you get a unique identifier for that user so you can properly handle their data operations.

I did a bit of searching and came across this Stackoverflow post/answer: iOS & node.js: how to verify passed access token?. User Gijs responded that different OAuth providers have different ways of authenticating an access token. So the idea would be to pass the token to your back-end server, verify it, and then get a unique id.

I decided to give this a shot and build a simple proof of concept. This is very rough, but gives you a basic idea of how you could do this. For my POC, I decided to use Christophe Coenraet's OpenFB library. It is a plugin-less JavaScript library that handles OAuth. Most recently I used ng-cordova-oauth as well, but I wanted to try something different. I created a quick Ionic application with two simple buttons:

iOS Simulator Screen Shot Jul 10, 2015, 1.44.58 PM

The first button fires off the OAuth process. I followed Christophe's directions and created my Facebook app first of course:

iOS Simulator Screen Shot Jul 10, 2015, 1.46.13 PM

The code behind this is pretty simple:

$scope.doLogin = function() {
    console.log("DoLogin");
      openFB.login(
      function(response) {
        if(response.status === 'connected') {
          $scope.token = response.authResponse.accessToken;
          console.log('Token stored: ',response);
          alert('Ok, try to make a call now');
        } else {
          alert('Facebook login failed: ' + response.error);
        }
      }, {scope: 'email'});                
}

Note I store the access token so I can use it later. Next - we need to call the server. If I remember right, Angular provides a way to modify all HTTP requests to include stuff. For now, I'm keeping it simple and just including the token as part of a form post.

$scope.doNode = function() {
    $http.post('http://localhost:3000/test1',{msg:"Hello",token:$scope.token}).
    success(function() {
      console.log('yes im ok');
    }).
    error(function(data,status,headers,config) {
      console.log('error from node');
      console.dir(arguments);      
    });
}

Ok, so far so good. Now let's turn to the Node side. As mentioned in the StackOverflow post I linked to above, you can make a request to graph.facebook.com with the token and if you get the proper response, you know you're good to go. I wrote a function I could use for middleware later on. It caches the test in the session scope which seems safe, but maybe that isn't a good idea.

function secure(req, result, next) {    
    if(req.session.tokenchecked) {
        next();   
    } else {
		console.log('need to check token ');
		var token = req.body.token;
		//check to ensure token is good
		https.get('https://graph.facebook.com/me?fields=email&access_token='+token, function(res) {
			var str = '';
			res.on('data', function(chunk) {
				str += chunk;
			})
			res.on('end', function() {
				var response = JSON.parse(str);
				console.dir(response);
				if(response.id) {
					console.log('good');
					req.session.tokenchecked = 1;
					next();
				} else {
					console.log('bad');
					result.send("0");
				}
			})
		}).end();
	
    }
}

Something to note here: I'm specifically asking for the email address back. My result looks like this:

{ email: 'raymondcamden@gmail.com', id: 'abigassnumber' }

In theory, the ID is unique enough. However, if I use multiple different oauth providers, I can instead use the email as a primary key. That would let you login via Facebook or Twitter and have the same data as long as you have the same email address being used for both accounts. Note that I do not actually store that email address. I would in a real app. Finally, here is the route I called from the mobile app:

app.post('/test1', secure, function(req, res) {
	console.log('attempting test1');
	var msg = req.body.msg;
	console.log('msg was '+msg);
	res.send("1");
});

It is pointless, but at least demonstrates using the secure function to wrap the route.

As I said - this is rough - but it seems to make sense to me. How about you?

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by chandramuralis posted on 7/10/2015 at 7:54 PM

I always had this in my mind but never got clarified. Thanks for this article.

Comment 2 by Haluk Sevener posted on 9/14/2015 at 5:38 PM

Thanks for your post. Your solution is good and pretty straightforward. But what about Google Sign In? You cannot just send back the token to your server, because Google offers different APIs for web and server clients and they cannot share tokens. How can you verify the user on both mobile client and server?

Comment 3 (In reply to #2) by Raymond Camden posted on 9/15/2015 at 1:16 PM

I haven't tested this, but I don't think you are correct. This doc (https://developers.google.c... shows an access token being returned, and the SO link I used above shows authenticating the AT for Google.

Comment 4 (In reply to #3) by Haluk Sevener posted on 9/17/2015 at 12:02 PM

wow wow wow, thanks for pointing out that doc page. I've been trying to figure out a way to solve this problem for days. I have non-trivial workflows in my mind which are hard to implement. But thanks to you I'm glad to see that one can easily verify a token, which was gathered via a JavaScript client, by just sending it back to the following Google API endpoint:

https://www.googleapis.com/...

You only need to change the value of access_token query parameter to what you've got and that's all!

Comment 5 (In reply to #4) by Raymond Camden posted on 9/17/2015 at 1:05 PM

Glad to help.

Comment 6 by Max Power posted on 5/15/2017 at 10:27 PM

Great post! Does this idea still make sense to you today? i'm looking around some similar concept and this idea make sense for me..

p.d.: Hopefully you will excuse my level of English.

Comment 7 (In reply to #6) by Raymond Camden posted on 5/16/2017 at 2:01 PM

Um, well, the Ionic code is somewhat old, and there are newer ways of doing social login (see Ionic's own User service). The general principle though remains the same I believe.

Comment 8 (In reply to #7) by Max Power posted on 5/17/2017 at 1:03 PM

Yes, of course, I meant the general principle of the idea. Thanks alot for the reply!

Comment 9 by Micaragua posted on 7/28/2017 at 8:03 PM

is it possible to use FB login code flow instead of access token one? in other words is it possible to obtain oauth code by the client, sent it to server, and exchange oauth code to access token ?

Comment 10 (In reply to #9) by Raymond Camden posted on 7/28/2017 at 10:03 PM

Not quite sure I get you - the user is logging in via FB.