diff --git a/lib/client.js b/lib/client.js index cf11dd66..a6cd66e5 100644 --- a/lib/client.js +++ b/lib/client.js @@ -105,10 +105,11 @@ async function get(config) { applyHttpOptionsCustom(client); client[custom.clock_tolerance] = config.clockTolerance; - if (config.idpLogout && !issuer.end_session_endpoint) { + if (config.idpLogout) { if ( config.auth0Logout || - url.parse(issuer.issuer).hostname.match('\\.auth0\\.com$') + (url.parse(issuer.issuer).hostname.match('\\.auth0\\.com$') && + config.auth0Logout !== false) ) { Object.defineProperty(client, 'endSessionUrl', { value(params) { @@ -130,7 +131,7 @@ async function get(config) { return url.format(parsedUrl); }, }); - } else { + } else if (!issuer.end_session_endpoint) { debug('the issuer does not support RP-Initiated Logout'); } } diff --git a/lib/config.js b/lib/config.js index 5a48bce6..06565d6b 100644 --- a/lib/config.js +++ b/lib/config.js @@ -93,7 +93,7 @@ const paramsSchema = Joi.object({ }) .default() .unknown(false), - auth0Logout: Joi.boolean().optional().default(false), + auth0Logout: Joi.boolean().optional(), tokenEndpointParams: Joi.object().optional(), authorizationParams: Joi.object({ response_type: Joi.string() diff --git a/test/client.tests.js b/test/client.tests.js index 7869f4ec..64e9dd5d 100644 --- a/test/client.tests.js +++ b/test/client.tests.js @@ -102,6 +102,95 @@ describe('client initialization', function () { }); }); + describe('auth0 logout option and discovery', function () { + const base = { + secret: '__test_session_secret__', + clientID: '__test_client_id__', + clientSecret: '__test_client_secret__', + issuerBaseURL: 'https://op.example.com', + baseURL: 'https://example.org', + idpLogout: true, + }; + + it('should use discovered logout endpoint by default', async function () { + const client = await getClient(getConfig(base)); + assert.equal(client.endSessionUrl({}), wellKnown.end_session_endpoint); + }); + + it('should use auth0 logout endpoint if configured', async function () { + const client = await getClient(getConfig({ ...base, auth0Logout: true })); + assert.equal( + client.endSessionUrl({}), + 'https://op.example.com/v2/logout?client_id=__test_client_id__' + ); + }); + + it('should use auth0 logout endpoint if domain is auth0.com', async function () { + nock('https://foo.auth0.com') + .get('/.well-known/openid-configuration') + .reply(200, { ...wellKnown, issuer: 'https://foo.auth0.com/' }); + const client = await getClient( + getConfig({ ...base, issuerBaseURL: 'https://foo.auth0.com' }) + ); + assert.equal( + client.endSessionUrl({}), + 'https://foo.auth0.com/v2/logout?client_id=__test_client_id__' + ); + }); + + it('should use auth0 logout endpoint if domain is auth0.com and configured', async function () { + nock('https://foo.auth0.com') + .get('/.well-known/openid-configuration') + .reply(200, { ...wellKnown, issuer: 'https://foo.auth0.com/' }); + const client = await getClient( + getConfig({ + ...base, + issuerBaseURL: 'https://foo.auth0.com', + auth0Logout: true, + }) + ); + assert.equal( + client.endSessionUrl({}), + 'https://foo.auth0.com/v2/logout?client_id=__test_client_id__' + ); + }); + + it('should not use discovered logout endpoint if domain is auth0.com but configured with auth0logout false', async function () { + nock('https://foo.auth0.com') + .get('/.well-known/openid-configuration') + .reply(200, { + ...wellKnown, + issuer: 'https://foo.auth0.com/', + end_session_endpoint: 'https://foo.auth0.com/oidc/logout', + }); + const client = await getClient( + getConfig({ + ...base, + issuerBaseURL: 'https://foo.auth0.com', + auth0Logout: false, + }) + ); + assert.equal( + client.endSessionUrl({}), + 'https://foo.auth0.com/oidc/logout' + ); + }); + + it('should create client with no end_session_endpoint', async function () { + nock('https://op2.example.com') + .get('/.well-known/openid-configuration') + .reply(200, { + ...wellKnown, + issuer: 'https://op2.example.com', + end_session_endpoint: undefined, + }); + const client = await getClient( + getConfig({ ...base, issuerBaseURL: 'https://op2.example.com' }) + ); + assert.throws(() => client.endSessionUrl({})); + }); + }); + describe('client respects httpTimeout configuration', function () { const config = getConfig({ secret: '__test_session_secret__', diff --git a/test/config.tests.js b/test/config.tests.js index 51bd9542..beb63559 100644 --- a/test/config.tests.js +++ b/test/config.tests.js @@ -86,12 +86,12 @@ describe('get config', () => { }); }); - it('auth0Logout and idpLogout should default to false', () => { + it('auth0Logout should default to undefined and idpLogout should default to false', () => { const config = getConfig(defaultConfig); assert.include(config, { - auth0Logout: false, idpLogout: false, }); + assert.isUndefined(config.auth0Logout); }); it('should not set auth0Logout to true when idpLogout is true', () => { @@ -100,9 +100,9 @@ describe('get config', () => { idpLogout: true, }); assert.include(config, { - auth0Logout: false, idpLogout: true, }); + assert.isUndefined(config.auth0Logout); }); it('should set default route paths', () => {