diff --git a/src/auth0-session/cookie-store.ts b/src/auth0-session/cookie-store.ts index ad6aca379..b09cd8010 100644 --- a/src/auth0-session/cookie-store.ts +++ b/src/auth0-session/cookie-store.ts @@ -166,10 +166,7 @@ export default class CookieStore { debug('clearing all matching session cookies'); for (const cookieName of Object.keys(cookies)) { if (cookieName.match(`^${sessionName}(?:\\.\\d)?$`)) { - clearCookie(res, cookieName, { - domain: cookieConfig.domain, - path: cookieConfig.path - }); + clearCookie(res, cookieName, cookieConfig); } } return; @@ -198,19 +195,13 @@ export default class CookieStore { setCookie(res, chunkCookieName, chunkValue, cookieOptions); } if (sessionName in cookies) { - clearCookie(res, sessionName, { - domain: cookieConfig.domain, - path: cookieConfig.path - }); + clearCookie(res, sessionName, cookieConfig); } } else { setCookie(res, sessionName, value, cookieOptions); for (const cookieName of Object.keys(cookies)) { if (cookieName.match(`^${sessionName}\\.\\d$`)) { - clearCookie(res, cookieName, { - domain: cookieConfig.domain, - path: cookieConfig.path - }); + clearCookie(res, cookieName, cookieConfig); } } } diff --git a/src/auth0-session/transient-store.ts b/src/auth0-session/transient-store.ts index 500ce4665..245f67ead 100644 --- a/src/auth0-session/transient-store.ts +++ b/src/auth0-session/transient-store.ts @@ -133,10 +133,10 @@ export default class TransientStore { */ read(key: string, req: IncomingMessage, res: ServerResponse): string | undefined { const cookie = getCookie(req, key); - const { domain, path } = this.config.session.cookie; + const cookieConfig = this.config.session.cookie; let value = getCookieValue(key, cookie, this.keyStore); - clearCookie(res, key, { domain, path }); + clearCookie(res, key, cookieConfig); if (this.config.legacySameSiteCookie) { const fallbackKey = `_${key}`; @@ -144,7 +144,7 @@ export default class TransientStore { const fallbackCookie = getCookie(req, fallbackKey); value = getCookieValue(fallbackKey, fallbackCookie, this.keyStore); } - clearCookie(res, fallbackKey, { domain, path }); + clearCookie(res, fallbackKey, cookieConfig); } return value; diff --git a/src/auth0-session/utils/cookies.ts b/src/auth0-session/utils/cookies.ts index 932f8e1a9..ea5e689b7 100644 --- a/src/auth0-session/utils/cookies.ts +++ b/src/auth0-session/utils/cookies.ts @@ -22,5 +22,18 @@ export const set = (res: ServerResponse, name: string, value: string, options: C }; export const clear = (res: ServerResponse, name: string, options: CookieSerializeOptions = {}): void => { - set(res, name, '', { ...options, maxAge: 0 }); + const { domain, path, secure, sameSite } = options; + const clearOptions: CookieSerializeOptions = { + domain, + path, + maxAge: 0 + }; + // If SameSite=None is set, the cookie Secure attribute must also be set (or the cookie will be blocked) + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none + if (sameSite === 'none') { + clearOptions.secure = secure; + clearOptions.sameSite = sameSite; + } + + set(res, name, '', clearOptions); }; diff --git a/tests/auth0-session/handlers/logout.test.ts b/tests/auth0-session/handlers/logout.test.ts index 858b77e81..74fafc459 100644 --- a/tests/auth0-session/handlers/logout.test.ts +++ b/tests/auth0-session/handlers/logout.test.ts @@ -157,4 +157,27 @@ describe('logout route', () => { expect(res.statusCode).toEqual(302); expect(res.headers.location).toEqual(returnTo); }); + + it('should clear session cookie when SameSite=None', async () => { + const baseURL = await setup( + { ...defaultConfig, idpLogout: false, session: { cookie: { sameSite: 'none' } } }, + { https: true } + ); + const cookieJar = await login(baseURL); + cookieJar.setCookieSync('foo=bar', baseURL); + + await get(baseURL, '/session', { cookieJar }); + expect(fromCookieJar(cookieJar, baseURL)).toMatchObject({ + appSession: expect.any(String), + foo: 'bar' + }); + + const { res } = await get(baseURL, '/logout', { cookieJar, fullResponse: true }); + const cookies = fromCookieJar(cookieJar, baseURL); + const sessionCookie = res.headers['set-cookie'].find((s: string) => /^appSession/.test(s)); + expect(sessionCookie).toMatch(/Secure/); + expect(sessionCookie).toMatch(/SameSite=None/); + expect(cookies).toHaveProperty('foo'); + expect(cookies).not.toHaveProperty('appSession'); + }); });