From 8301a804b4ee08f205a501681da9e587e6156fd3 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Wed, 22 Jul 2026 10:39:33 +0200 Subject: [PATCH] feat(editor): Localize insecure connection warning (no-changelog) (#34636) Co-authored-by: Claude Opus 4.8 --- .../frontend/@n8n/i18n/src/locales/en.json | 5 ++ packages/frontend/@n8n/stores/package.json | 1 + .../@n8n/stores/src/settings.store.test.ts | 52 +++++++++++++++++++ .../@n8n/stores/src/settings.store.ts | 18 ++++--- pnpm-lock.yaml | 3 ++ 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index f20b71d959b..9ab4b2e7e14 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -3072,6 +3072,11 @@ "saveButton.saved": "Saved", "saveButton.saving": "Saving", "settings": "Settings", + "settings.authCookie.insecureConnection.title": "Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari.", + "settings.authCookie.insecureConnection.fixIntro": "To fix this, please consider the following options:", + "settings.authCookie.insecureConnection.option.tls": "Setup TLS/HTTPS (recommended), or", + "settings.authCookie.insecureConnection.option.localhost": "If you are running this locally, and not using Safari, try using localhost instead", + "settings.authCookie.insecureConnection.option.disableSecureCookie": "If you prefer to disable this security feature (not recommended), set the environment variable {envVar} to false", "settings.communityNodes": "Community nodes", "settings.communityNodes.empty.title": "Supercharge your workflows with community nodes", "settings.communityNodes.empty.verified.only.title": "Supercharge your workflows with verified community nodes", diff --git a/packages/frontend/@n8n/stores/package.json b/packages/frontend/@n8n/stores/package.json index 45810e600c6..f6a7e967d29 100644 --- a/packages/frontend/@n8n/stores/package.json +++ b/packages/frontend/@n8n/stores/package.json @@ -37,6 +37,7 @@ }, "dependencies": { "@n8n/api-types": "workspace:*", + "@n8n/i18n": "workspace:*", "@n8n/permissions": "workspace:*", "@n8n/rest-api-client": "workspace:*", "n8n-workflow": "workspace:*", diff --git a/packages/frontend/@n8n/stores/src/settings.store.test.ts b/packages/frontend/@n8n/stores/src/settings.store.test.ts index 4619fb97784..4412ff9a994 100644 --- a/packages/frontend/@n8n/stores/src/settings.store.test.ts +++ b/packages/frontend/@n8n/stores/src/settings.store.test.ts @@ -1,5 +1,7 @@ import type { FrontendSettings } from '@n8n/api-types'; +import { i18n } from '@n8n/i18n'; import { createPinia, setActivePinia } from 'pinia'; +import type { MockInstance } from 'vitest'; import { mock } from 'vitest-mock-extended'; import { useSettingsStore } from './settings.store'; @@ -167,6 +169,56 @@ describe('settings.store', () => { }); }); + describe('insecure connection warning', () => { + let writeSpy: MockInstance<(...text: string[]) => void>; + + beforeEach(() => { + writeSpy = vi.spyOn(document, 'write').mockImplementation(() => {}); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it('should render the localized warning over an insecure, non-localhost origin', async () => { + vi.stubGlobal('location', { protocol: 'http:', hostname: 'n8n.example.com' }); + + getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: true } }); + + await useSettingsStore().getSettings(); + + expect(writeSpy).toHaveBeenCalledTimes(1); + const markup = writeSpy.mock.calls[0][0]; + expect(markup).toContain(i18n.baseText('settings.authCookie.insecureConnection.title')); + expect(markup).toContain('N8N_SECURE_COOKIE'); + expect(markup).toContain('http://localhost:5678'); + }); + + it('should not render the warning over localhost in a non-Safari browser', async () => { + vi.stubGlobal('location', { protocol: 'http:', hostname: 'localhost' }); + vi.stubGlobal('navigator', { + userAgent: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0', + }); + + getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: true } }); + + await useSettingsStore().getSettings(); + + expect(writeSpy).not.toHaveBeenCalled(); + }); + + it('should not render the warning when the cookie is not secure', async () => { + vi.stubGlobal('location', { protocol: 'http:', hostname: 'n8n.example.com' }); + + getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: false } }); + + await useSettingsStore().getSettings(); + + expect(writeSpy).not.toHaveBeenCalled(); + }); + }); + describe('getSettings', () => { describe('telemetry', () => { it('should fetch settings and call sessionStarted if telemetry is enabled', async () => { diff --git a/packages/frontend/@n8n/stores/src/settings.store.ts b/packages/frontend/@n8n/stores/src/settings.store.ts index 9bc2d6d60e1..cbc251a03ce 100644 --- a/packages/frontend/@n8n/stores/src/settings.store.ts +++ b/packages/frontend/@n8n/stores/src/settings.store.ts @@ -5,6 +5,7 @@ import { type FrontendModuleSettings, type WorkflowReviewsPolicy, } from '@n8n/api-types'; +import { i18n } from '@n8n/i18n'; import { makeRestApiRequest } from '@n8n/rest-api-client'; import * as aiUsageApi from '@n8n/rest-api-client/api/ai-usage'; import * as eventsApi from '@n8n/rest-api-client/api/events'; @@ -22,19 +23,20 @@ import { useRootStore } from './useRootStore'; /** * Full-page warning rendered when the instance requires a secure cookie but the * page is served over an insecure origin (or via Safari, which drops the cookie). + * The copy is localized; the structural markup and inline styles stay in code + * because this is written via `document.write` before the Vue app mounts. */ -const INSECURE_CONNECTION_WARNING = ` +const buildInsecureConnectionWarning = () => `

🚫

-

Your n8n server is configured to use a secure cookie,
however you are either visiting this via an insecure URL, or using Safari. -

+

${i18n.baseText('settings.authCookie.insecureConnection.title')}


- To fix this, please consider the following options: + ${i18n.baseText('settings.authCookie.insecureConnection.fixIntro')}
    -
  • Setup TLS/HTTPS (recommended), or
  • -
  • If you are running this locally, and not using Safari, try using localhost instead
  • -
  • If you prefer to disable this security feature (not recommended), set the environment variable N8N_SECURE_COOKIE to false
  • +
  • ${i18n.baseText('settings.authCookie.insecureConnection.option.tls')}
  • +
  • ${i18n.baseText('settings.authCookie.insecureConnection.option.localhost', { interpolate: { localhostUrl: 'http://localhost:5678' } })}
  • +
  • ${i18n.baseText('settings.authCookie.insecureConnection.option.disableSecureCookie', { interpolate: { envVar: 'N8N_SECURE_COOKIE' } })}
`; @@ -290,7 +292,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => { location.protocol === 'http:' && (!['localhost', '127.0.0.1'].includes(location.hostname) || browser.name === 'Safari') ) { - document.write(INSECURE_CONNECTION_WARNING); + document.write(buildInsecureConnectionWarning()); return; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcce2b46554..abfadacabbb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5383,6 +5383,9 @@ importers: '@n8n/api-types': specifier: workspace:* version: link:../../../@n8n/api-types + '@n8n/i18n': + specifier: workspace:* + version: link:../i18n '@n8n/permissions': specifier: workspace:* version: link:../../../@n8n/permissions