Skip to content

Commit

Permalink
Adding access_token validation for RS256 id_token's (#709)
Browse files Browse the repository at this point in the history
* validating  tokens according to response_type and access_tokens

* add missing test

* test naming
  • Loading branch information
luisrudge authored Mar 22, 2018
1 parent eea118b commit 9a52fcd
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 11 deletions.
2 changes: 1 addition & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ <h2>Console:</h2>
domain: 'brucke.auth0.com',
redirectUri: 'https://localhost:3000/example/',
clientID: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
responseType: 'token',
responseType: 'token id_token',
plugins: [
new CordovaAuth0Plugin()
]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"license": "MIT",
"dependencies": {
"base64-js": "^1.2.0",
"idtoken-verifier": "^1.1.2",
"idtoken-verifier": "^1.2.0",
"qs": "^6.4.0",
"superagent": "^3.8.2",
"url-join": "^1.1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/helper/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ function buildResponse(error, description) {
};
}

function invalidJwt(description) {
function invalidToken(description) {
return buildResponse('invalid_token', description);
}

module.exports = {
buildResponse: buildResponse,
invalidJwt: invalidJwt
invalidToken: invalidToken
};
19 changes: 17 additions & 2 deletions src/web-auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,22 @@ WebAuth.prototype.validateAuthenticationResponse = function(options, parsedHash,
payload
) {
if (!validationError) {
return callback(null, payload);
if (!parsedHash.access_token) {
return callback(null, payload);
}
// here we're absolutely sure that the id_token's alg is RS256
// and that the id_token is valid, so we can check the access_token
return new IdTokenVerifier().validateAccessToken(
parsedHash.access_token,
'RS256',
payload.at_hash,
function(err) {
if (err) {
return callback(error.invalidToken(err.message));
}
return callback(null, payload);
}
);
}
if (validationError.error !== 'invalid_token') {
return callback(validationError);
Expand Down Expand Up @@ -307,7 +322,7 @@ WebAuth.prototype.validateToken = function(token, nonce, cb) {

verifier.verify(token, nonce, function(err, payload) {
if (err) {
return cb(error.invalidJwt(err.message));
return cb(error.invalidToken(err.message));
}

cb(null, payload);
Expand Down
5 changes: 5 additions & 0 deletions test/plugins/cordova.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var IdTokenVerifier = require('idtoken-verifier');
var expect = require('expect.js');
var stub = require('sinon').stub;

Expand Down Expand Up @@ -100,6 +101,9 @@ describe('auth0.plugins.cordova', function() {

context('PopupHandler', function() {
beforeEach(function() {
stub(IdTokenVerifier.prototype, 'validateAccessToken', function(at, alg, atHash, cb) {
cb(null);
});
var _this = this;
this.events = {};
var webAuth = new WebAuth({
Expand Down Expand Up @@ -132,6 +136,7 @@ describe('auth0.plugins.cordova', function() {
});

afterEach(function() {
IdTokenVerifier.prototype.validateAccessToken.restore();
delete global.window;
this.events = null;
this.popupHandler = null;
Expand Down
127 changes: 125 additions & 2 deletions test/web-auth/web-auth.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var expect = require('expect.js');
var stub = require('sinon').stub;
var spy = require('sinon').spy;
var request = require('superagent');
var IdTokenVerifier = require('idtoken-verifier');

var storage = require('../../src/helper/storage');
var windowHelper = require('../../src/helper/window');
Expand Down Expand Up @@ -271,9 +272,18 @@ describe('auth0.WebAuth', function() {

beforeEach(function() {
spy(ssodata, 'set');
stub(IdTokenVerifier.prototype, 'validateAccessToken', function(at, alg, atHash, cb) {
cb(null);
});
});
afterEach(function() {
ssodata.set.restore();
if (IdTokenVerifier.prototype.validateAccessToken.restore) {
IdTokenVerifier.prototype.validateAccessToken.restore();
}
if (WebAuth.prototype.validateToken.restore) {
WebAuth.prototype.validateToken.restore();
}
});

it('should parse a valid hash without id_token', function(done) {
Expand Down Expand Up @@ -307,6 +317,120 @@ describe('auth0.WebAuth', function() {
}
); // eslint-disable-line
});
it('should return the id_token payload when there is no access_token', function(done) {
var webAuth = new WebAuth({
domain: 'brucke.auth0.com',
redirectUri: 'http://example.com/callback',
clientID: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
responseType: 'id_token',
__disableExpirationCheck: true
});

var data = webAuth.parseHash(
{
hash: '#state=foo&token_type=Bearer&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wMy0xNFQxNjozNDo1Ni40MjNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6Ims1dTNvMmZpQUE4WHdlWEVFWDYwNEtDd0Nqemp0TVU2IiwiaWF0IjoxNTIxMDQ1Mjk2LCJleHAiOjE1MjEwODEyOTYsImF0X2hhc2giOiJjZHVrb2FVc3dNOWJvX3l6cmdWY3J3Iiwibm9uY2UiOiJsRkNuSTguY3JSVGRIZmRvNWsuek1YZlIzMTg1NmdLeiJ9.U4_F5Zw6xYVoHGiiem1wjz7i9eRaSOrt-L1e6hlu3wmqA-oNuVqf1tEYD9u0z5AbXXbQSr491A3VvUbLKjws13XETcljhaqigZ9q4HBpmzPlrUGmPreBLVQgGOaq5NVAViFTvORxYCMFLlc-SE6QI6xWF0AhFpoW7-hkOcOzXWAXqhkMgwAfjJ9aeOzSBgblmtx4duyNESBRefd3XPQrakWjGIqH3dFdc-lDFbY76eSLYfBi4AH-yim4egzB6LYOC-e2huZcHdmRAmEQaKZ7D7COBiGsgAPVGyjZtqfSQ2CRwNrAbxDwi8BqlLhQePOs6d3hqV-3OPLfdE6dUFh2DQ',
nonce: 'lFCnI8.crRTdHfdo5k.zMXfR31856gKz'
},
function(err, data) {
expect(err).to.be(null);
expect(data).to.be.eql({
accessToken: null,
idToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wMy0xNFQxNjozNDo1Ni40MjNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6Ims1dTNvMmZpQUE4WHdlWEVFWDYwNEtDd0Nqemp0TVU2IiwiaWF0IjoxNTIxMDQ1Mjk2LCJleHAiOjE1MjEwODEyOTYsImF0X2hhc2giOiJjZHVrb2FVc3dNOWJvX3l6cmdWY3J3Iiwibm9uY2UiOiJsRkNuSTguY3JSVGRIZmRvNWsuek1YZlIzMTg1NmdLeiJ9.U4_F5Zw6xYVoHGiiem1wjz7i9eRaSOrt-L1e6hlu3wmqA-oNuVqf1tEYD9u0z5AbXXbQSr491A3VvUbLKjws13XETcljhaqigZ9q4HBpmzPlrUGmPreBLVQgGOaq5NVAViFTvORxYCMFLlc-SE6QI6xWF0AhFpoW7-hkOcOzXWAXqhkMgwAfjJ9aeOzSBgblmtx4duyNESBRefd3XPQrakWjGIqH3dFdc-lDFbY76eSLYfBi4AH-yim4egzB6LYOC-e2huZcHdmRAmEQaKZ7D7COBiGsgAPVGyjZtqfSQ2CRwNrAbxDwi8BqlLhQePOs6d3hqV-3OPLfdE6dUFh2DQ',
idTokenPayload: {
nickname: 'johnfoo',
name: '[email protected]',
picture: 'https://s.gravatar.com/avatar/38fa002423bd8c941c6ed0588b60ffed?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fjo.png',
updated_at: '2018-03-14T16:34:56.423Z',
email: '[email protected]',
email_verified: false,
iss: 'https://brucke.auth0.com/',
sub: 'auth0|5a2054ff45157711be8182f4',
aud: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
iat: 1521045296,
exp: 1521081296,
at_hash: 'cdukoaUswM9bo_yzrgVcrw',
nonce: 'lFCnI8.crRTdHfdo5k.zMXfR31856gKz'
},
appState: null,
refreshToken: null,
state: 'foo',
expiresIn: null,
tokenType: 'Bearer',
scope: null
});
done();
}
); // eslint-disable-line
});
it('should return the id_token payload when there is a valid access_token', function(done) {
var webAuth = new WebAuth({
domain: 'brucke.auth0.com',
redirectUri: 'http://example.com/callback',
clientID: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
responseType: 'token id_token',
__disableExpirationCheck: true
});

var data = webAuth.parseHash(
{
hash: '#state=foo&token_type=Bearer&access_token=L11oiFDHj3zmZid1AnsEuggXcMfjqe0X&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wMy0yMVQyMDowNDo0Mi40OTNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6Ims1dTNvMmZpQUE4WHdlWEVFWDYwNEtDd0Nqemp0TVU2IiwiaWF0IjoxNTIxNjYyNjgyLCJleHAiOjE1MjE2OTg2ODIsImF0X2hhc2giOiJKS2NaM3hTQ2NGVEE5NkxuQ3lJX0FRIiwibm9uY2UiOiJLdlhoc1VIc2VJSEl5emF1X2JVflJHQ2t1RUFDTE5HaiJ9.UbiWFikCkoX-m22mFnXJhKMY8M9BGMDJqZZ5J-iUAQwOmD-33-zX-AjSbD6zL6sOJoKJratJLtLa90tE3sDeokI9c8GE_JonfeF95knVPAx99tD5eCIJabV8HN_K1rfcgI_ed9v8RKQD9_dRkwUMHgXyceWeijnA9k8jG-pe1iXAtnn386G5s6fj-do8SUvC2MFWNmD5VhkW-CyEg_Chui8BoOSM9d7liMZRQkgKA2aGl5t2qqvOu0ZNJwaWoeQ5T0R-h2Yk6Om_alFKyLdZXsZY2LRYQdbk4nEgxY59241HPZGHYOTJN5uLlbcxKyouTyM7Gt4dE76wyRh9kBr47A',
nonce: 'KvXhsUHseIHIyzau_bU~RGCkuEACLNGj'
},
function(err, data) {
expect(err).to.be(null);
expect(data).to.be.eql({
accessToken: 'L11oiFDHj3zmZid1AnsEuggXcMfjqe0X',
idToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wMy0yMVQyMDowNDo0Mi40OTNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6Ims1dTNvMmZpQUE4WHdlWEVFWDYwNEtDd0Nqemp0TVU2IiwiaWF0IjoxNTIxNjYyNjgyLCJleHAiOjE1MjE2OTg2ODIsImF0X2hhc2giOiJKS2NaM3hTQ2NGVEE5NkxuQ3lJX0FRIiwibm9uY2UiOiJLdlhoc1VIc2VJSEl5emF1X2JVflJHQ2t1RUFDTE5HaiJ9.UbiWFikCkoX-m22mFnXJhKMY8M9BGMDJqZZ5J-iUAQwOmD-33-zX-AjSbD6zL6sOJoKJratJLtLa90tE3sDeokI9c8GE_JonfeF95knVPAx99tD5eCIJabV8HN_K1rfcgI_ed9v8RKQD9_dRkwUMHgXyceWeijnA9k8jG-pe1iXAtnn386G5s6fj-do8SUvC2MFWNmD5VhkW-CyEg_Chui8BoOSM9d7liMZRQkgKA2aGl5t2qqvOu0ZNJwaWoeQ5T0R-h2Yk6Om_alFKyLdZXsZY2LRYQdbk4nEgxY59241HPZGHYOTJN5uLlbcxKyouTyM7Gt4dE76wyRh9kBr47A',
idTokenPayload: {
nickname: 'johnfoo',
name: '[email protected]',
picture: 'https://s.gravatar.com/avatar/38fa002423bd8c941c6ed0588b60ffed?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fjo.png',
updated_at: '2018-03-21T20:04:42.493Z',
email: '[email protected]',
email_verified: false,
iss: 'https://brucke.auth0.com/',
sub: 'auth0|5a2054ff45157711be8182f4',
aud: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
iat: 1521662682,
exp: 1521698682,
at_hash: 'JKcZ3xSCcFTA96LnCyI_AQ',
nonce: 'KvXhsUHseIHIyzau_bU~RGCkuEACLNGj'
},
appState: null,
refreshToken: null,
state: 'foo',
expiresIn: null,
tokenType: 'Bearer',
scope: null
});
done();
}
); // eslint-disable-line
});
it('should validate an access_token when available', function(done) {
var webAuth = new WebAuth({
domain: 'brucke.auth0.com',
redirectUri: 'http://example.com/callback',
clientID: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
responseType: 'token id_token',
__disableExpirationCheck: true
});
IdTokenVerifier.prototype.validateAccessToken.restore();

var data = webAuth.parseHash(
{
hash: '#state=foo&token_type=Bearer&access_token=YTvJcYrrZYHUXLZK5leLnfmD5ZIA_EA&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wMy0xNFQxNjozNDo1Ni40MjNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6Ims1dTNvMmZpQUE4WHdlWEVFWDYwNEtDd0Nqemp0TVU2IiwiaWF0IjoxNTIxMDQ1Mjk2LCJleHAiOjE1MjEwODEyOTYsImF0X2hhc2giOiJjZHVrb2FVc3dNOWJvX3l6cmdWY3J3Iiwibm9uY2UiOiJsRkNuSTguY3JSVGRIZmRvNWsuek1YZlIzMTg1NmdLeiJ9.U4_F5Zw6xYVoHGiiem1wjz7i9eRaSOrt-L1e6hlu3wmqA-oNuVqf1tEYD9u0z5AbXXbQSr491A3VvUbLKjws13XETcljhaqigZ9q4HBpmzPlrUGmPreBLVQgGOaq5NVAViFTvORxYCMFLlc-SE6QI6xWF0AhFpoW7-hkOcOzXWAXqhkMgwAfjJ9aeOzSBgblmtx4duyNESBRefd3XPQrakWjGIqH3dFdc-lDFbY76eSLYfBi4AH-yim4egzB6LYOC-e2huZcHdmRAmEQaKZ7D7COBiGsgAPVGyjZtqfSQ2CRwNrAbxDwi8BqlLhQePOs6d3hqV-3OPLfdE6dUFh2DQ',
nonce: 'lFCnI8.crRTdHfdo5k.zMXfR31856gKz'
},
function(err, data) {
expect(err).to.be.eql({
error: 'invalid_token',
errorDescription: 'Invalid access_token'
});
done();
}
); // eslint-disable-line
});

context('when there is a transaction', function() {
it('should return transaction.appState', function(done) {
Expand Down Expand Up @@ -824,7 +948,7 @@ describe('auth0.WebAuth', function() {
domain: 'mdocs_2.auth0.com',
redirectUri: 'http://example.com/callback',
clientID: '0HP71GSd6PuoRYJ3DXKdiXCUUdGmBbup',
responseType: 'token'
responseType: 'id_token'
});

var data = webAuth.parseHash(
Expand Down Expand Up @@ -880,7 +1004,6 @@ describe('auth0.WebAuth', function() {
},
function(err, data) {
expect(err).to.be.eql(expectedError);
WebAuth.prototype.validateToken.restore();
done();
}
);
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1956,9 +1956,9 @@ husky@^0.13.3:
is-ci "^1.0.9"
normalize-path "^1.0.0"

idtoken-verifier@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/idtoken-verifier/-/idtoken-verifier-1.1.2.tgz#bd5125aaccc221c1e0c57c9393dc68c0f7134b69"
idtoken-verifier@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/idtoken-verifier/-/idtoken-verifier-1.2.0.tgz#4654f1f07ab7a803fc9b1b8b36057e2a87ad8b09"
dependencies:
base64-js "^1.2.0"
crypto-js "^3.1.9-1"
Expand Down

0 comments on commit 9a52fcd

Please sign in to comment.