Merge pull request #39 from tbetbetbe/node-auth-correct-jwt-get-request-metadata

Node auth correct jwt get request metadata
This commit is contained in:
Tim Emiola
2015-05-21 08:25:38 -07:00
5 changed files with 84 additions and 77 deletions

View File

@@ -49,10 +49,10 @@ JWTAccess.prototype.createScopedRequired = function() {
* Get a non-expired access token, after refreshing if necessary
*
* @param {string} authURI the URI being authorized
* @param {function} accessTokenFn a callback invoked with the access
* token.
* @param {function} metadataCb a callback invoked with the jwt
* request metadata.
*/
JWTAccess.prototype.getRequestMetadata = function(authURI, accessTokenFn) {
JWTAccess.prototype.getRequestMetadata = function(authURI, metadataCb) {
var iat = Math.floor(new Date().getTime() / 1000);
var exp = iat + 3600; // 3600 seconds = 1 hour
@@ -75,12 +75,12 @@ JWTAccess.prototype.getRequestMetadata = function(authURI, accessTokenFn) {
secret: this.key
};
// Sign the jwt and invoke accessTokenFn with it.
this._signJWT(assertion, function(err, signedJWT) {
// Sign the jwt and invoke metadataCb with it.
return this._signJWT(assertion, function(err, signedJWT) {
if (!err) {
accessTokenFn(null, {'Authorization': 'Bearer ' + signedJWT});
return metadataCb(null, {'Authorization': 'Bearer ' + signedJWT});
} else {
accessTokenFn(err, null);
return metadataCb(err, null);
}
});
};
@@ -92,26 +92,26 @@ JWTAccess.prototype.getRequestMetadata = function(authURI, accessTokenFn) {
*/
JWTAccess.prototype.fromJSON = function(json, opt_callback) {
var that = this;
var callback = opt_callback || noop;
var done = opt_callback || noop;
if (!json) {
callback(new Error(
done(new Error(
'Must pass in a JSON object containing the service account auth settings.'));
return;
}
if (!json.client_email) {
callback(new Error(
done(new Error(
'The incoming JSON object does not contain a client_email field'));
return;
}
if (!json.private_key) {
callback(new Error(
done(new Error(
'The incoming JSON object does not contain a private_key field'));
return;
}
// Extract the relevant information from the json key file.
that.email = json.client_email;
that.key = json.private_key;
callback();
done();
};
/**
@@ -121,10 +121,10 @@ JWTAccess.prototype.fromJSON = function(json, opt_callback) {
*/
JWTAccess.prototype.fromStream = function(stream, opt_callback) {
var that = this;
var callback = opt_callback || noop;
var done = opt_callback || noop;
if (!stream) {
process.nextTick(function() {
callback(
done(
new Error('Must pass in a stream containing the service account auth settings.'));
});
return;
@@ -139,7 +139,7 @@ JWTAccess.prototype.fromStream = function(stream, opt_callback) {
var data = JSON.parse(s);
that.fromJSON(data, opt_callback);
} catch (err) {
callback(err);
done(err);
}
});
};
@@ -155,9 +155,9 @@ JWTAccess.prototype.fromStream = function(stream, opt_callback) {
*/
JWTAccess.prototype._signJWT = function(assertion, signedJwtFn) {
try {
signedJwtFn(null, jws.sign(assertion));
return signedJwtFn(null, jws.sign(assertion));
} catch (err) {
signedJwtFn(err);
return signedJwtFn(err);
}
};

View File

@@ -19,8 +19,10 @@
var Auth2Client = require('./oauth2client.js');
var gToken = require('gtoken');
var JWTAccess = require('./jwtaccess.js');
var noop = require('lodash.noop');
var util = require('util');
/**
* JWT service account credentials.
*
@@ -53,13 +55,6 @@ function JWT(email, keyFile, key, scopes, subject) {
*/
util.inherits(JWT, Auth2Client);
// Executes the given callback if it is not null.
function callback(c, err, res) {
if (c) {
c(err, res);
}
}
/**
* Creates a copy of the credential with the specified scopes.
* @param {(string|array)=} scopes List of requested scopes or a single scope.
@@ -73,16 +68,16 @@ JWT.prototype.createScoped = function(scopes) {
* Obtains the metadata to be sent with the request.
*
* @param {string} opt_uri the URI being authorized.
* @param {function} cb_with_metadata
* @param {function} metadataCb
*/
JWT.prototype.getRequestMetadata = function(opt_uri, cb_with_metadata) {
JWT.prototype.getRequestMetadata = function(opt_uri, metadataCb) {
if (this.createScopedRequired() && opt_uri) {
// no scopes have been set, but a uri has been provided. Use JWTAccess credentials.
var alt = new JWTAccess(this.email, this.key);
alt.getRequestMetadata(opt_uri, cb_with_metadata);
return alt.getRequestMetadata(opt_uri, metadataCb);
} else {
JWT.super_.prototype.getRequestMetadata.call(
this, opt_uri, cb_with_metadata);
return JWT.super_.prototype.getRequestMetadata.call(
this, opt_uri, metadataCb);
}
};
@@ -112,6 +107,7 @@ JWT.prototype.createScopedRequired = function() {
*/
JWT.prototype.authorize = function(opt_callback) {
var that = this;
var done = opt_callback || noop;
that.refreshToken_(null, function(err, result) {
if (!err) {
@@ -120,7 +116,7 @@ JWT.prototype.authorize = function(opt_callback) {
that.key = that.gtoken.key;
that.email = that.gtoken.iss;
}
callback(opt_callback, err, result);
done(err, result);
});
};
@@ -132,12 +128,14 @@ JWT.prototype.authorize = function(opt_callback) {
* @private
*/
JWT.prototype.refreshToken_ = function(ignored_, opt_callback) {
this._createGToken(function(err, gToken) {
var done = opt_callback || noop;
return this._createGToken(function(err, gToken) {
if (err) {
callback(opt_callback, err);
return done(err);
} else {
gToken.getToken(function (err, token) {
callback(opt_callback, err, {
return gToken.getToken(function (err, token) {
return done(err, {
access_token: token,
token_type: 'Bearer',
expiry_date: gToken.expires_at
@@ -155,25 +153,26 @@ JWT.prototype.refreshToken_ = function(ignored_, opt_callback) {
*/
JWT.prototype.fromJSON = function(json, opt_callback) {
var that = this;
var done = opt_callback || noop;
if (!json) {
callback(opt_callback, new Error(
done(new Error(
'Must pass in a JSON object containing the service account auth settings.'));
return;
}
if (!json.client_email) {
callback(opt_callback, new Error(
done(new Error(
'The incoming JSON object does not contain a client_email field'));
return;
}
if (!json.private_key) {
callback(opt_callback, new Error(
done(new Error(
'The incoming JSON object does not contain a private_key field'));
return;
}
// Extract the relevant information from the json key file.
that.email = json.client_email;
that.key = json.private_key;
callback(opt_callback);
done();
};
/**
@@ -183,10 +182,11 @@ JWT.prototype.fromJSON = function(json, opt_callback) {
*/
JWT.prototype.fromStream = function(stream, opt_callback) {
var that = this;
var done = opt_callback || noop;
if (!stream) {
process.nextTick(function() {
callback(
opt_callback,
done(
new Error('Must pass in a stream containing the service account auth settings.'));
});
return;
@@ -201,7 +201,7 @@ JWT.prototype.fromStream = function(stream, opt_callback) {
var data = JSON.parse(s);
that.fromJSON(data, opt_callback);
} catch (err) {
callback(opt_callback, err);
done(err);
}
});
};
@@ -213,7 +213,7 @@ JWT.prototype.fromStream = function(stream, opt_callback) {
*/
JWT.prototype._createGToken = function(callback) {
if (this.gtoken) {
callback(null, this.gtoken);
return callback(null, this.gtoken);
} else {
this.gtoken = this.gToken({
iss: this.email,
@@ -222,7 +222,7 @@ JWT.prototype._createGToken = function(callback) {
keyFile: this.keyFile,
key: this.key
});
callback(null, this.gtoken);
return callback(null, this.gtoken);
}
};

View File

@@ -16,11 +16,12 @@
'use strict';
var querystring = require('querystring');
var AuthClient = require('./authclient.js');
var util = require('util');
var PemVerifier = require('./../pemverifier.js');
var LoginTicket = require('./loginticket.js');
var noop = require('lodash.noop');
var PemVerifier = require('./../pemverifier.js');
var querystring = require('querystring');
var util = require('util');
var certificateCache = null;
var certificateExpiry = null;
@@ -149,9 +150,8 @@ OAuth2Client.prototype.getToken = function(code, opt_callback) {
tokens.expiry_date = ((new Date()).getTime() + (tokens.expires_in * 1000));
delete tokens.expires_in;
}
if (opt_callback) {
opt_callback(err, tokens, response);
}
var done = opt_callback || noop;
done(err, tokens, response);
});
};
@@ -171,7 +171,7 @@ OAuth2Client.prototype.refreshToken_ = function(refresh_token, opt_callback) {
};
// request for new token
this.transporter.request({
return this.transporter.request({
method: 'POST',
uri: uri,
form: values,
@@ -181,9 +181,8 @@ OAuth2Client.prototype.refreshToken_ = function(refresh_token, opt_callback) {
tokens.expiry_date = ((new Date()).getTime() + (tokens.expires_in * 1000));
delete tokens.expires_in;
}
if (opt_callback) {
opt_callback(err, tokens, response);
}
var done = opt_callback || noop;
done(err, tokens, response);
});
};
@@ -262,14 +261,14 @@ OAuth2Client.prototype.getAccessToken = function(callback) {
* {Authorization: 'Bearer <access_token_value>'}
*
* @param {string} opt_uri the Uri being authorized
* @param {function} cb_with_metadata the func described above
* @param {function} metadataCb the func described above
*/
OAuth2Client.prototype.getRequestMetadata = function(opt_uri, cb_with_metadata) {
OAuth2Client.prototype.getRequestMetadata = function(opt_uri, metadataCb) {
var that = this;
var thisCreds = this.credentials;
if (!thisCreds.access_token && !thisCreds.refresh_token) {
return cb_with_metadata(new Error('No access or refresh token is set.'), null);
return metadataCb(new Error('No access or refresh token is set.'), null);
}
// if no expiry time, assume it's not expired
@@ -279,15 +278,15 @@ OAuth2Client.prototype.getRequestMetadata = function(opt_uri, cb_with_metadata)
if (thisCreds.access_token && !isTokenExpired) {
thisCreds.token_type = thisCreds.token_type || 'Bearer';
var headers = {'Authorization': thisCreds.token_type + ' ' + thisCreds.access_token };
return cb_with_metadata(null, headers , null);
return metadataCb(null, headers , null);
}
this.refreshToken_(thisCreds.refresh_token, function(err, tokens, response) {
return this.refreshToken_(thisCreds.refresh_token, function(err, tokens, response) {
if (err) {
cb_with_metadata(err, null, response);
return metadataCb(err, null, response);
} else {
if (!tokens || (tokens && !tokens.access_token)) {
return cb_with_metadata(new Error('Could not refresh access token.'), null, response);
return metadataCb(new Error('Could not refresh access token.'), null, response);
}
var credentials = that.credentials;
@@ -295,7 +294,7 @@ OAuth2Client.prototype.getRequestMetadata = function(opt_uri, cb_with_metadata)
tokens.refresh_token = credentials.refresh_token;
that.credentials = tokens;
var headers = {'Authorization': credentials.token_type + ' ' + tokens.access_token };
cb_with_metadata(null, headers , response);
return metadataCb(err, headers , response);
}
});
};
@@ -340,23 +339,23 @@ OAuth2Client.prototype.request = function(opts, callback) {
var that = this;
// Hook the callback routine to call the _postRequest method.
var my_callback = function(e, b, r) {
that._postRequest(e, b, r, callback);
var postRequestCb = function(err, body, resp) {
that._postRequest(err, body, resp, callback);
};
var cb_with_auth = function(err, headers, response) {
var authCb = function(err, headers, response) {
if (err) {
my_callback(err, null, response);
postRequestCb(err, null, response);
} else {
if (headers) {
opts.headers = opts.headers || {};
opts.headers.Authorization = headers.Authorization;
}
return that._makeRequest(opts, my_callback);
return that._makeRequest(opts, postRequestCb);
}
};
var unusedUri = null;
return this.getRequestMetadata(unusedUri, cb_with_auth);
return this.getRequestMetadata(unusedUri, authCb);
};
/**

View File

@@ -69,7 +69,7 @@ describe('JWT auth client', function() {
key: 'private-key-data',
iss: 'foo@subjectaccount.com',
getToken: function(opt_callback) {
opt_callback(null, 'initial-access-token');
return opt_callback(null, 'initial-access-token');
}
};
};
@@ -124,7 +124,7 @@ describe('JWT auth client', function() {
var want = 'abc123';
jwt.gtoken = {
getToken: function(callback) {
callback(null, want);
return callback(null, want);
}
};
@@ -159,18 +159,20 @@ describe('JWT auth client', function() {
var wanted_token = 'abc123';
jwt.gtoken = {
getToken: function(callback) {
callback(null, wanted_token);
return callback(null, wanted_token);
}
};
var want = 'Bearer ' + wanted_token;
var retValue = 'dummy';
var unusedUri = null;
jwt.getRequestMetadata(unusedUri, function(err, got) {
var res = jwt.getRequestMetadata(unusedUri, function(err, got) {
assert.strictEqual(null, err, 'no error was expected: got\n' + err);
assert.strictEqual(want, got.Authorization,
'the authorization header was wrong: ' + got.Authorization);
done();
return retValue;
});
assert.strictEqual(res, retValue);
});
});
@@ -193,7 +195,8 @@ describe('JWT auth client', function() {
};
var testUri = 'http:/example.com/my_test_service';
jwt.getRequestMetadata(testUri, function(err, got) {
var retValue = 'dummy';
var res = jwt.getRequestMetadata(testUri, function(err, got) {
assert.strictEqual(null, err, 'no error was expected: got\n' + err);
assert.notStrictEqual(null, got, 'the creds should be present');
var decoded = jws.decode(got.Authorization.replace('Bearer ', ''));
@@ -201,7 +204,9 @@ describe('JWT auth client', function() {
assert.strictEqual(email, decoded.payload.sub);
assert.strictEqual(testUri, decoded.payload.aud);
done();
return retValue;
});
assert.strictEqual(res, retValue);
});
});
@@ -252,7 +257,7 @@ describe('JWT auth client', function() {
jwt.gtoken = {
getToken: function(callback) {
callback(null, 'abc123');
return callback(null, 'abc123');
}
};
@@ -336,7 +341,7 @@ describe('JWT auth client', function() {
jwt.gtoken = {
getToken: function(callback) {
callback(null, 'token');
return callback(null, 'token');
},
expires_at: dateInMillis
};

View File

@@ -43,7 +43,8 @@ describe('.getRequestMetadata', function() {
var auth = new googleAuth();
var client = new auth.JWTAccess(email, keys['private']);
var expect_authorization = function(err, creds) {
var retValue = 'dummy';
var expectAuth = function(err, creds) {
assert.strictEqual(null, err, 'no error was expected: got\n' + err);
assert.notStrictEqual(null, creds, 'an creds object should be present');
var decoded = jws.decode(creds.Authorization.replace('Bearer ', ''));
@@ -51,8 +52,10 @@ describe('.getRequestMetadata', function() {
assert.strictEqual(email, decoded.payload.sub);
assert.strictEqual(testUri, decoded.payload.aud);
done();
return retValue;
};
client.getRequestMetadata(testUri, expect_authorization);
var res = client.getRequestMetadata(testUri, expectAuth);
assert.strictEqual(res, retValue);
});
});