diff --git a/example/index.html b/example/index.html
index 8b3691c8..98b47ead 100644
--- a/example/index.html
+++ b/example/index.html
@@ -203,7 +203,7 @@
Console:
domain: 'brucke.auth0.com',
redirectUri: 'https://localhost:3000/example/',
clientID: 'k5u3o2fiAA8XweXEEX604KCwCjzjtMU6',
- responseType: 'token',
+ responseType: 'token id_token',
plugins: [
new CordovaAuth0Plugin()
]
diff --git a/package.json b/package.json
index a1884b01..a588d74a 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/helper/error.js b/src/helper/error.js
index c146e4c4..bf367424 100644
--- a/src/helper/error.js
+++ b/src/helper/error.js
@@ -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
};
diff --git a/src/web-auth/index.js b/src/web-auth/index.js
index 0b4e20e5..af373df6 100644
--- a/src/web-auth/index.js
+++ b/src/web-auth/index.js
@@ -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);
@@ -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);
diff --git a/test/plugins/cordova.test.js b/test/plugins/cordova.test.js
index 400870c8..b03f8410 100644
--- a/test/plugins/cordova.test.js
+++ b/test/plugins/cordova.test.js
@@ -1,3 +1,4 @@
+var IdTokenVerifier = require('idtoken-verifier');
var expect = require('expect.js');
var stub = require('sinon').stub;
@@ -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({
@@ -132,6 +136,7 @@ describe('auth0.plugins.cordova', function() {
});
afterEach(function() {
+ IdTokenVerifier.prototype.validateAccessToken.restore();
delete global.window;
this.events = null;
this.popupHandler = null;
diff --git a/test/web-auth/web-auth.test.js b/test/web-auth/web-auth.test.js
index 4836d303..05740594 100644
--- a/test/web-auth/web-auth.test.js
+++ b/test/web-auth/web-auth.test.js
@@ -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');
@@ -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) {
@@ -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: 'johnfoo@gmail.com',
+ 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: 'johnfoo@gmail.com',
+ 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: 'johnfoo@gmail.com',
+ 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: 'johnfoo@gmail.com',
+ 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) {
@@ -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(
@@ -880,7 +1004,6 @@ describe('auth0.WebAuth', function() {
},
function(err, data) {
expect(err).to.be.eql(expectedError);
- WebAuth.prototype.validateToken.restore();
done();
}
);
diff --git a/yarn.lock b/yarn.lock
index bdcbf396..15fc2fdc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"