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.
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.