diff --git a/index.d.ts b/index.d.ts index 1472d34d..49704c59 100644 --- a/index.d.ts +++ b/index.d.ts @@ -450,6 +450,11 @@ interface ConfigParams { * Relative path to the application callback to process the response from the authorization server. */ callback?: string; + + /** + * Configuration parameters used for the transaction cookie. + */ + transactionCookie: Pick; }; /** diff --git a/lib/config.js b/lib/config.js index c47cae54..4ef93e37 100644 --- a/lib/config.js +++ b/lib/config.js @@ -77,6 +77,14 @@ const paramsSchema = Joi.object({ }) .default() .unknown(false), + transactionCookie: Joi.object({ + sameSite: Joi.string() + .valid('Lax', 'Strict', 'None') + .optional() + .default(Joi.ref('...session.cookie.sameSite')), + }) + .default() + .unknown(false), auth0Logout: Joi.boolean().optional().default(false), tokenEndpointParams: Joi.object().optional(), authorizationParams: Joi.object({ diff --git a/lib/context.js b/lib/context.js index c6c3bb09..99b771e0 100644 --- a/lib/context.js +++ b/lib/context.js @@ -259,7 +259,7 @@ class ResponseContext { sameSite: options.authorizationParams.response_mode === 'form_post' ? 'None' - : config.session.cookie.sameSite, + : config.transactionCookie.sameSite, value: JSON.stringify(authVerification), }); diff --git a/test/config.tests.js b/test/config.tests.js index 401d8830..002b6dd5 100644 --- a/test/config.tests.js +++ b/test/config.tests.js @@ -185,6 +185,9 @@ describe('get config', () => { assert.deepInclude(config, { secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + transactionCookie: { + sameSite: 'Strict', + }, session: { name: '__test_custom_session_name__', rollingDuration: 1234567890, @@ -202,6 +205,61 @@ describe('get config', () => { }); }); + it('should set default transaction cookie sameSite configuration', () => { + const config = getConfig({ + ...defaultConfig, + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + }); + + assert.deepInclude(config, { + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + transactionCookie: { + sameSite: 'Lax', + }, + }); + }); + + it('should set default transaction cookie sameSite configuration from session cookie configuration', () => { + const config = getConfig({ + ...defaultConfig, + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + session: { + cookie: { + sameSite: 'Strict', + }, + }, + }); + + assert.deepInclude(config, { + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + transactionCookie: { + sameSite: 'Strict', + }, + }); + }); + + it('should set custom transaction cookie configuration', () => { + const config = getConfig({ + ...defaultConfig, + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + transactionCookie: { + sameSite: 'Strict', + }, + session: { + cookie: { + sameSite: 'Lax', + }, + }, + }); + + assert.deepInclude(config, { + secret: ['__test_session_secret_1__', '__test_session_secret_2__'], + transactionCookie: { + sameSite: 'Strict', + }, + }); + }); + it('should fail when the baseURL is http and cookie is secure', function () { assert.throws(() => { getConfig({ diff --git a/test/login.tests.js b/test/login.tests.js index ca593f9a..f28cc787 100644 --- a/test/login.tests.js +++ b/test/login.tests.js @@ -363,16 +363,36 @@ describe('auth', () => { assert.isDefined(fetchFromAuthCookie(res, 'code_verifier')); }); - it('should respect sameSite when response_mode is not form_post', async () => { + it('should respect session.cookie.sameSite when transaction.sameSite is not set and response_mode is not form_post', async () => { server = await createServer( auth({ ...defaultConfig, clientSecret: '__test_client_secret__', + authorizationParams: { + response_mode: 'query', + response_type: 'code', + }, session: { cookie: { sameSite: 'Strict', }, }, + }) + ); + const res = await request.get('/login', { baseUrl, followRedirect: false }); + assert.equal(res.statusCode, 302); + + assert.include(fetchAuthCookie(res), 'SameSite=Strict'); + }); + + it('should respect transactionCookie.sameSite when response_mode is not form_post', async () => { + server = await createServer( + auth({ + ...defaultConfig, + clientSecret: '__test_client_secret__', + transactionCookie: { + sameSite: 'Strict', + }, authorizationParams: { response_mode: 'query', response_type: 'code', @@ -389,10 +409,8 @@ describe('auth', () => { server = await createServer( auth({ ...defaultConfig, - session: { - cookie: { - sameSite: 'Strict', - }, + transactionCookie: { + sameSite: 'Strict', }, }) );