From fcfd2befca1cffe909e0b423ad2a79f2082b33a7 Mon Sep 17 00:00:00 2001 From: adamjmcgrath Date: Fri, 10 Mar 2023 12:01:10 +0000 Subject: [PATCH 1/2] Always honor auth0Logout config --- src/auth0-session/client.ts | 10 +++-- src/auth0-session/config.ts | 2 +- src/auth0-session/get-config.ts | 2 +- src/config.ts | 2 +- tests/auth0-session/client.test.ts | 60 ++++++++++++++++++++++++++++++ tests/auth0-session/config.test.ts | 14 +++---- tests/handlers/logout.test.ts | 33 +++++++++++++--- 7 files changed, 103 insertions(+), 20 deletions(-) diff --git a/src/auth0-session/client.ts b/src/auth0-session/client.ts index 481060490..d0ce9f32f 100644 --- a/src/auth0-session/client.ts +++ b/src/auth0-session/client.ts @@ -109,8 +109,12 @@ export default function get(config: Config, { name, version }: Telemetry): Clien ); client[custom.clock_tolerance] = config.clockTolerance; - if (config.idpLogout && !issuer.end_session_endpoint) { - if (config.auth0Logout || (url.parse(issuer.metadata.issuer).hostname as string).match('\\.auth0\\.com$')) { + if (config.idpLogout) { + if ( + config.auth0Logout || + ((url.parse(issuer.metadata.issuer).hostname as string).match('\\.auth0\\.com$') && + config.auth0Logout !== false) + ) { Object.defineProperty(client, 'endSessionUrl', { value(params: EndSessionParameters) { const { id_token_hint, post_logout_redirect_uri, ...extraParams } = params; @@ -128,7 +132,7 @@ export default function get(config: Config, { name, version }: Telemetry): Clien return url.format(parsedUrl); } }); - } else { + } else if (!issuer.end_session_endpoint) { debug('the issuer does not support RP-Initiated Logout'); } } diff --git a/src/auth0-session/config.ts b/src/auth0-session/config.ts index df45f82d2..e0dd4394b 100644 --- a/src/auth0-session/config.ts +++ b/src/auth0-session/config.ts @@ -21,7 +21,7 @@ export interface Config { /** * Boolean value to enable Auth0's logout feature. */ - auth0Logout: boolean; + auth0Logout?: boolean; /** * URL parameters used when redirecting users to the authorization server to log in. diff --git a/src/auth0-session/get-config.ts b/src/auth0-session/get-config.ts index 488f49acd..3defc218f 100644 --- a/src/auth0-session/get-config.ts +++ b/src/auth0-session/get-config.ts @@ -53,7 +53,7 @@ const paramsSchema = Joi.object({ }) .default() .unknown(false), - auth0Logout: Joi.boolean().optional().default(false), + auth0Logout: Joi.boolean().optional(), authorizationParams: Joi.object({ response_type: Joi.string().optional().valid('id_token', 'code id_token', 'code').default('id_token'), scope: Joi.string() diff --git a/src/config.ts b/src/config.ts index 0e86aa370..8e97584b0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -27,7 +27,7 @@ export interface BaseConfig { * Boolean value to enable Auth0's proprietary logout feature. * Since this SDK is for Auth0, it's set to `true`by default. */ - auth0Logout: boolean; + auth0Logout?: boolean; /** * URL parameters used when redirecting users to the authorization server to log in. diff --git a/tests/auth0-session/client.test.ts b/tests/auth0-session/client.test.ts index b35c85704..8d3603304 100644 --- a/tests/auth0-session/client.test.ts +++ b/tests/auth0-session/client.test.ts @@ -105,6 +105,66 @@ describe('clientFactory', function () { expect(client.id_token_signed_response_alg).toEqual('RS256'); }); + it('should use discovered logout endpoint by default', async function () { + const client = await getClient({ ...defaultConfig, idpLogout: true }); + expect(client.endSessionUrl({})).toEqual('https://op.example.com/session/end?client_id=__test_client_id__'); + }); + + it('should use auth0 logout endpoint if configured', async function () { + const client = await getClient({ ...defaultConfig, idpLogout: true, auth0Logout: true }); + expect(client.endSessionUrl({})).toEqual('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({ ...defaultConfig, idpLogout: true, issuerBaseURL: 'https://foo.auth0.com' }); + expect(client.endSessionUrl({})).toEqual('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({ + ...defaultConfig, + issuerBaseURL: 'https://foo.auth0.com', + idpLogout: true, + auth0Logout: true + }); + expect(client.endSessionUrl({})).toEqual('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({ + ...defaultConfig, + issuerBaseURL: 'https://foo.auth0.com', + idpLogout: true, + auth0Logout: false + }); + expect(client.endSessionUrl({})).toEqual('https://foo.auth0.com/oidc/logout?client_id=__test_client_id__'); + }); + + 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({ ...defaultConfig, issuerBaseURL: 'https://op2.example.com' }); + expect(() => client.endSessionUrl({})).toThrowError(); + }); + it('should create custom logout for auth0', async function () { nock('https://test.eu.auth0.com') .get('/.well-known/openid-configuration') diff --git a/tests/auth0-session/config.test.ts b/tests/auth0-session/config.test.ts index ff9a26f2a..252018eef 100644 --- a/tests/auth0-session/config.test.ts +++ b/tests/auth0-session/config.test.ts @@ -64,12 +64,10 @@ describe('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); - expect(config).toMatchObject({ - auth0Logout: false, - idpLogout: false - }); + expect(config.idpLogout).toBe(false); + expect(config.auth0Logout).toBeUndefined(); }); it('should not set auth0Logout to true when idpLogout is true', () => { @@ -77,10 +75,8 @@ describe('Config', () => { ...defaultConfig, idpLogout: true }); - expect(config).toMatchObject({ - auth0Logout: false, - idpLogout: true - }); + expect(config.idpLogout).toBe(true); + expect(config.auth0Logout).toBeUndefined(); }); it('should set default route paths', () => { diff --git a/tests/handlers/logout.test.ts b/tests/handlers/logout.test.ts index c8e30dd56..b043e7dff 100644 --- a/tests/handlers/logout.test.ts +++ b/tests/handlers/logout.test.ts @@ -7,7 +7,7 @@ import { setup, teardown, login } from '../fixtures/setup'; describe('logout handler', () => { afterEach(teardown); - test('should redirect to the identity provider', async () => { + test('should redirect to auth0', async () => { const baseUrl = await setup(withoutApi); const cookieJar = await login(baseUrl); @@ -30,7 +30,7 @@ describe('logout handler', () => { }); }); - test('should pass logout params to the identity provider', async () => { + test('should pass logout params to auth0', async () => { const baseUrl = await setup(withoutApi, { logoutOptions: { logoutParams: { foo: 'bar' } } }); const cookieJar = await login(baseUrl); @@ -74,7 +74,30 @@ describe('logout handler', () => { }); }); - test('should use end_session_endpoint if available', async () => { + test('should use end_session_endpoint when configured', async () => { + const baseUrl = await setup( + { ...withoutApi, auth0Logout: false }, + { + discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + } + ); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'])).toMatchObject({ + host: 'my-end-session-endpoint', + pathname: '/logout' + }); + }); + + test('should use auth0 logout by default even when end_session_endpoint is discovered', async () => { const baseUrl = await setup(withoutApi, { discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } }); @@ -89,8 +112,8 @@ describe('logout handler', () => { expect(statusCode).toBe(302); expect(parseUrl(headers['location'])).toMatchObject({ - host: 'my-end-session-endpoint', - pathname: '/logout' + host: 'acme.auth0.local', + pathname: '/v2/logout' }); }); From ba6829e1f6707f4932c09e99fc902f838a19f201 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Fri, 10 Mar 2023 16:07:59 +0000 Subject: [PATCH 2/2] Update tests/auth0-session/client.test.ts --- tests/auth0-session/client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auth0-session/client.test.ts b/tests/auth0-session/client.test.ts index 8d3603304..cf925fd4d 100644 --- a/tests/auth0-session/client.test.ts +++ b/tests/auth0-session/client.test.ts @@ -136,7 +136,7 @@ describe('clientFactory', function () { expect(client.endSessionUrl({})).toEqual('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 () { + it('should 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, {