I'm having a heck of a time here trying to use google OAuth to authenticate users in my node express app. I can successfully do the OAuth, which returns a response like so:
{
access_token: 'token string',
id_token: 'id.string',
expires_in: 3599,
token_type: "Bearer"
}
This all makes sense, but I can't for the life of me figure out how to decode the JWT. I am a bit inexperienced in all this, so this is all a bit foreign to me.
Following the instructions listed here: https://developers.google.com/accounts/docs/OAuth2Login#validatinganidtoken I am attempting to decode the JWT locally in my node app.
I installed https://github.com/hokaccha/node-jwt-simple in my node environment.
And I'm pretty certain I need to use this certificate (https://www.googleapis.com/oauth2/v1/certs) in all this somehow to decode it, but I am at a bit of a loss here. I don't really understand how I get the certificate into my node app, and after that how to use it with node-jwt-simple. And I also don't really understand how I know when I need to pull a fresh certificate, vs using a cached one.
Anyone out there with some experience in this that can help me out?
Thanks for any help. I'm totally at a loss at this point.
** Update **
So I have made some progress... Kind of. By calling jwt.decode(id_token, certificate, true); I am able to successfully decode the token. Even if the certificate var is an empty object {}. This leaves me with 3 questions still. 1: What is the best way to get the certificate into my express app using the url from google? 2: How will I know when I need to pull in a fresh version of it? 3: It seems like passing in true for noVerify (3rd arg in jwt.decode) is a terrible idea. How can I get that to work without passing that in? It looks like perhaps jwt-simple is expecting hs256 and the token is using rs256.
Again, I'm super inexperienced in this, so I may be way off base here.
* UPDATE * Thanks to the help from Nat, I was able to get this working! I think I tried every single JWT and JWS node module out there. What I finally landed on is as follows: I found that none of the modules that I looked at did quite what I wanted out of the box. I created the following jwt decoding helper methods that I am using to decode the id_token, so I can get the kid from the header.
module.exports = {
decodeJwt: function (token) {
var segments = token.split('.');
if (segments.length !== 3) {
throw new Error('Not enough or too many segments');
}
// All segment should be base64
var headerSeg = segments[0];
var payloadSeg = segments[1];
var signatureSeg = segments[2];
// base64 decode and parse JSON
var header = JSON.parse(base64urlDecode(headerSeg));
var payload = JSON.parse(base64urlDecode(payloadSeg));
return {
header: header,
payload: payload,
signature: signatureSeg
}
}
}
function base64urlDecode(str) {
return new Buffer(base64urlUnescape(str), 'base64').toString();
};
function base64urlUnescape(str) {
str += Array(5 - str.length % 4).join('=');
return str.replace(/\-/g, '+').replace(/_/g, '/');
}
I am using this decoding to determine if I need to pull in a new public cert from: https://www.googleapis.com/oauth2/v1/certs
Then I am using that public cert and node-jws (https://github.com/brianloveswords/node-jws) jws.verify(id_token, cert) to verify the signature!
Hooray! Thanks again for the extra explanation you gave in your response. That went a long way in helping me understand what I was even trying to do. Hope this might help others too.