From 453b5fc088bbbc1bc2717b0db3a70f643e218b27 Mon Sep 17 00:00:00 2001 From: Harshit Chandra Date: Tue, 28 Oct 2025 13:45:08 +0530 Subject: [PATCH] feat: add configurable session cookie name (#5425) Added support for overriding the default session cookie name using the `INFRA.SESSION_COOKIE_NAME` config or the `SESSION_COOKIE_NAME` environment variable. This helps compatibility with proxies or load balancers that cannot handle cookie names containing dots. --- Co-authored-by: mirarifhasan Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../src/infra-config/helper.ts | 5 ++++ .../src/infra-config/infra-config.service.ts | 5 ++++ packages/hoppscotch-backend/src/main.ts | 7 ++++- .../src/types/InfraConfig.ts | 1 + packages/hoppscotch-sh-admin/locales/en.json | 3 ++ .../src/components/settings/AuthToken.vue | 14 ++++++++++ .../src/composables/useConfigHandler.ts | 28 +++++++++++++++++-- .../src/helpers/configs.ts | 8 ++++++ 8 files changed, 68 insertions(+), 3 deletions(-) diff --git a/packages/hoppscotch-backend/src/infra-config/helper.ts b/packages/hoppscotch-backend/src/infra-config/helper.ts index 496cbb66..76b2b1da 100644 --- a/packages/hoppscotch-backend/src/infra-config/helper.ts +++ b/packages/hoppscotch-backend/src/infra-config/helper.ts @@ -127,6 +127,11 @@ export async function getDefaultInfraConfigs(): Promise { value: encrypt(randomBytes(32).toString('hex')), isEncrypted: true, }, + { + name: InfraConfigEnum.SESSION_COOKIE_NAME, + value: null, + isEncrypted: false, + }, { name: InfraConfigEnum.TOKEN_SALT_COMPLEXITY, value: '10', diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts index dc0dc4b6..28086d27 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts @@ -734,6 +734,11 @@ export class InfraConfigService implements OnModuleInit { return fail(); break; + case InfraConfigEnum.SESSION_COOKIE_NAME: + // Allow empty to fall back to default; otherwise enforce allowed characters + if (value && !/^[A-Za-z0-9_-]+$/.test(value)) return fail(); + break; + default: break; } diff --git a/packages/hoppscotch-backend/src/main.ts b/packages/hoppscotch-backend/src/main.ts index 3dd11812..7c392557 100644 --- a/packages/hoppscotch-backend/src/main.ts +++ b/packages/hoppscotch-backend/src/main.ts @@ -49,8 +49,13 @@ async function bootstrap() { app.use( session({ + // Allow overriding the default cookie name 'connect.sid' (which contains a dot). + // Some proxies/load balancers (like older Kong versions) cannot hash cookie names with dots, + // so we allow setting an alternative name via the INFRA.SESSION_COOKIE_NAME configuration. + name: + configService.get('INFRA.SESSION_COOKIE_NAME') || 'connect.sid', secret: - configService.get('INFRA.SESSION_SECRET') || + configService.get('INFRA.SESSION_SECRET') || crypto.randomBytes(16).toString('hex'), }), ); diff --git a/packages/hoppscotch-backend/src/types/InfraConfig.ts b/packages/hoppscotch-backend/src/types/InfraConfig.ts index 3f5c56d1..0a86e088 100644 --- a/packages/hoppscotch-backend/src/types/InfraConfig.ts +++ b/packages/hoppscotch-backend/src/types/InfraConfig.ts @@ -4,6 +4,7 @@ export enum InfraConfigEnum { JWT_SECRET = 'JWT_SECRET', SESSION_SECRET = 'SESSION_SECRET', + SESSION_COOKIE_NAME = 'SESSION_COOKIE_NAME', TOKEN_SALT_COMPLEXITY = 'TOKEN_SALT_COMPLEXITY', MAGIC_LINK_TOKEN_VALIDITY = 'MAGIC_LINK_TOKEN_VALIDITY', REFRESH_TOKEN_VALIDITY = 'REFRESH_TOKEN_VALIDITY', diff --git a/packages/hoppscotch-sh-admin/locales/en.json b/packages/hoppscotch-sh-admin/locales/en.json index d972d9d0..5ec5955b 100644 --- a/packages/hoppscotch-sh-admin/locales/en.json +++ b/packages/hoppscotch-sh-admin/locales/en.json @@ -52,6 +52,9 @@ "refresh_token_validity": "Refresh Token Validity (in milliseconds)", "access_token_validity": "Access Token Validity (in milliseconds)", "session_secret": "Session Secret", + "session_cookie_name": "Session Cookie Name (optional)", + "session_cookie_name_help": "Only letters, numbers, underscore, and hyphen. Leave empty to use default 'connect.sid'.", + "session_cookie_name_invalid": "Invalid cookie name. Only letters, numbers, underscore, and hyphen allowed.", "update_failure": "Failed to update token configurations!!" }, "update_failure": "Failed to update authentication provider configurations!!" diff --git a/packages/hoppscotch-sh-admin/src/components/settings/AuthToken.vue b/packages/hoppscotch-sh-admin/src/components/settings/AuthToken.vue index 8fd40fb4..3749bd47 100644 --- a/packages/hoppscotch-sh-admin/src/components/settings/AuthToken.vue +++ b/packages/hoppscotch-sh-admin/src/components/settings/AuthToken.vue @@ -133,6 +133,20 @@ +
+ + +

+ {{ t('configs.auth_providers.token.session_cookie_name_help') }} +

+
diff --git a/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts b/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts index 3b4d4f6b..df3b0f53 100644 --- a/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts +++ b/packages/hoppscotch-sh-admin/src/composables/useConfigHandler.ts @@ -28,12 +28,19 @@ import { MICROSOFT_CONFIGS, MOCK_SERVER_CONFIGS, ServerConfigs, + TOKEN_VALIDATION_CONFIGS, UpdatedConfigs, } from '~/helpers/configs'; import { getCompiledErrorMessage } from '~/helpers/errors'; import { useToast } from './toast'; import { useClientHandler } from './useClientHandler'; +const COOKIE_NAME_REGEX = /^[A-Za-z0-9_-]+$/; + +const OPTIONAL_TOKEN_FIELD_KEYS = new Set( + TOKEN_VALIDATION_CONFIGS.filter((cfg) => cfg.optional).map((cfg) => cfg.key) +); + /** Composable that handles all operations related to server configurations * @param updatedConfigs A Config Object containing the updated configs */ @@ -154,6 +161,7 @@ export function useConfigHandler(updatedConfigs?: ServerConfigs) { InfraConfigEnum.AccessTokenValidity ), session_secret: getFieldValue(InfraConfigEnum.SessionSecret), + session_cookie_name: getFieldValue(InfraConfigEnum.SessionCookieName), }, }, dataSharingConfigs: { @@ -286,8 +294,12 @@ export function useConfigHandler(updatedConfigs?: ServerConfigs) { // This section has no enabled property, so we check fields directly // for a valid number (>0) or non-empty string - if (section.name === 'token') - return Object.values(section.fields).some(isFieldNotValid); + if (section.name === 'token') { + return Object.entries(section.fields).some( + ([key, value]) => + !OPTIONAL_TOKEN_FIELD_KEYS.has(key) && isFieldNotValid(value) + ); + } // For rate limit section, we want to check if the values are not valid numbers // and not empty strings @@ -573,6 +585,14 @@ export function useConfigHandler(updatedConfigs?: ServerConfigs) { const sessionSecret = String( updatedConfigs?.tokenConfigs.fields.session_secret ); + const sessionCookieName = String( + updatedConfigs?.tokenConfigs.fields.session_cookie_name || '' + ); + // Validate cookie name: allow empty (falls back to default), else enforce pattern + if (sessionCookieName && !COOKIE_NAME_REGEX.test(sessionCookieName)) { + toast.error(t('configs.auth_providers.token.session_cookie_name_invalid')); + return false; + } if ( isFieldEmpty(jwtSecret) || isFieldEmpty(tokenSaltComplexity) || @@ -610,6 +630,10 @@ export function useConfigHandler(updatedConfigs?: ServerConfigs) { name: InfraConfigEnum.SessionSecret, value: sessionSecret, }, + { + name: InfraConfigEnum.SessionCookieName, + value: sessionCookieName, + }, ]; return executeMutation( diff --git a/packages/hoppscotch-sh-admin/src/helpers/configs.ts b/packages/hoppscotch-sh-admin/src/helpers/configs.ts index 980a8405..89e95ab6 100644 --- a/packages/hoppscotch-sh-admin/src/helpers/configs.ts +++ b/packages/hoppscotch-sh-admin/src/helpers/configs.ts @@ -67,6 +67,7 @@ export type ServerConfigs = { refresh_token_validity: string; access_token_validity: string; session_secret: string; + session_cookie_name: string; }; }; @@ -116,6 +117,8 @@ export type ConfigSection = { export type Config = { name: InfraConfigEnum; key: string; + // Marks fields that are optional and should be excluded from mandatory validation + optional?: boolean; }; export const GOOGLE_CONFIGS: Config[] = [ @@ -258,6 +261,11 @@ export const TOKEN_VALIDATION_CONFIGS: Config[] = [ name: InfraConfigEnum.SessionSecret, key: 'session_secret', }, + { + name: InfraConfigEnum.SessionCookieName, + key: 'session_cookie_name', + optional: true, + }, { name: InfraConfigEnum.TokenSaltComplexity, key: 'token_salt_complexity',