Search code examples
iosnode.jsauthenticationjson-web-token

jwt authentication in iOS client nodejs server via third party authenticator


I am trying to wrap my head around using json webtoken (jwt) based authentication on a server coupled to using a third party (say google) to authenticate the user. Originally I've managed to build my own login and jwt handling scheme with jsonwebtoken on my nodejs server, but we need a client running on an iOS system to interact with it and started looking around for a solution where we don't have to code so much client code (requesting new token when expired etc.) and thought that we would use a third party library to do this for us.

The thing is I did not find anything that would do this for us. I found libraries that could handle connecting the client to a google api for the client, I found user identification handled by google, but didn't find anything that would handle actually getting a jwt that the server would except as a genuine user.

My question is essentially this: we have an iOS client and a nodejs server and would like to use google to authenticate our users and have the client call api-s on our nodejs server, with as much of the authentication process handled by some third party library (google's?), how should we get around to this?

As a note, I've seen passport but that seems to operate with sessions only, and I would have to solve the jwt handling by myself were I to use that.


Solution

  • The iOS part is not ready, but I managed to use google to authenticate and authorize without a session in the browser. The idea is, that the client logs in to google (see here for web app) and google graciously also gives you a token with the login, which will be good for the server. On the nodejs side I used passport and the google-id-token strategy (see on github). There are quite a few strategies for google out there, but this one works. Although, this has a shortcoming, it can't accept the token in the header, but I fixed that in a pull request (see here).

    Since I had a bit of a problem of how to use the User.findOrCreate part of all the passport examples, I'll put in my code here that covers a full working example:

    var passport = require('passport');
    var GoogleTokenStrategy = require(passport-google-id-token)
    
    passport.use(new GoogleTokenStrategy({
        clientID: config.googleAuth.clientID,
        clientSecret: config.googleAuth.clientSecret,
      },
      function(parsedToken, googleId, done) {
        console.log(parsedToken);
        console.log(googleId);
    
          User.findOne({ 'google.id': googleId }, function (err, user) {
              if (!user) {
                  var testuser = new User({ 
                    name: parsedToken.payload.name,
                    givenName : parsedToken.payload.givenName,
                    familyName : parsedToken.payload.familyName,
                    nameunderscore : parsedToken.payload.name.split(' ').join("_"),
    
                  admin: false,
              email: parsedToken.payload.email,
              settings: {save_folder:"default"},
              'google.id' : googleId,
              'google.email' :  parsedToken.payload.email,
              });
            testuser.save(function(err) {})
    
            }
            return done(err, user);
          });
      }
    ));
    

    User comes from mongodb in a separate js:

    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    module.exports = mongoose.model('User', new Schema({ 
        name: String,
        nameunderscore : String,
        givenName: String,
    
        familyName: String,
        admin: Boolean,
        settings: {
            save_folder: String
            },
        email: String,
        google: {
            id: String,
            email: String
            }
    }));
    

    And this is how I added the passport strategy to a router (note that session is set to false):

    var apiRoutes = express.Router();
    apiRoutes.use(passport.authenticate('google-id-token',{ session: false }));
    

    Now every call to any route in apiRoutes must send on id_token with a valid google token to get access.