feat(core)!: Set the secure flag on issued cookies (#8812)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-03-05 18:57:41 +01:00 committed by GitHub
parent 2b0e14e936
commit 0818824a72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 44 additions and 12 deletions

View file

@ -2,6 +2,16 @@
This list shows all the versions which include breaking changes and how to upgrade.
## 1.32.0
### What changed?
N8n auth cookie has `Secure` flag set by default now.
### When is action necessary?
If you are running n8n without HTTP**S** on a domain other than `localhost`, you need to either setup HTTPS, or you can disable the secure flag by setting the env variable `N8N_SECURE_COOKIE` to `false`.
## 1.27.0
### What changed?

View file

@ -82,6 +82,7 @@ export class AuthService {
maxAge: this.jwtExpiration * Time.seconds.toMilliseconds,
httpOnly: true,
sameSite: 'lax',
secure: config.getEnv('secure_cookie'),
});
}

View file

@ -15,6 +15,7 @@ if (inE2ETests) {
process.env.N8N_LOG_LEVEL = 'silent';
process.env.N8N_PUBLIC_API_DISABLED = 'true';
process.env.SKIP_STATISTICS_EVENTS = 'true';
process.env.N8N_SECURE_COOKIE = 'false';
} else {
dotenv.config();
}

View file

@ -538,6 +538,12 @@ export const schema = {
env: 'N8N_PROTOCOL',
doc: 'HTTP Protocol via which n8n can be reached',
},
secure_cookie: {
doc: 'This sets the `Secure` flag on n8n auth cookie',
format: Boolean,
default: true,
env: 'N8N_SECURE_COOKIE',
},
ssl_key: {
format: String,
default: '',

View file

@ -90,6 +90,7 @@ describe('AuthService', () => {
httpOnly: true,
maxAge: 604800000,
sameSite: 'lax',
secure: false,
});
});
});
@ -177,6 +178,7 @@ describe('AuthService', () => {
httpOnly: true,
maxAge: 604800000,
sameSite: 'lax',
secure: false,
});
});

View file

@ -1,7 +1,7 @@
import type { CookieOptions, Response } from 'express';
import type { Response } from 'express';
import { Container } from 'typedi';
import jwt from 'jsonwebtoken';
import { mock, anyObject, captor } from 'jest-mock-extended';
import { mock, anyObject } from 'jest-mock-extended';
import type { PublicUser } from '@/Interfaces';
import type { User } from '@db/entities/User';
import { MeController } from '@/controllers/me.controller';
@ -11,10 +11,10 @@ import { UserService } from '@/services/user.service';
import { ExternalHooks } from '@/ExternalHooks';
import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License';
import { badPasswords } from '../shared/testData';
import { mockInstance } from '../../shared/mocking';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { UserRepository } from '@/databases/repositories/user.repository';
import { badPasswords } from '../shared/testData';
import { mockInstance } from '../../shared/mocking';
describe('MeController', () => {
const externalHooks = mockInstance(ExternalHooks);
@ -63,10 +63,16 @@ describe('MeController', () => {
expect(userService.update).toHaveBeenCalled();
const cookieOptions = captor<CookieOptions>();
expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, 'signed-token', cookieOptions);
expect(cookieOptions.value.httpOnly).toBe(true);
expect(cookieOptions.value.sameSite).toBe('lax');
expect(res.cookie).toHaveBeenCalledWith(
AUTH_COOKIE_NAME,
'signed-token',
expect.objectContaining({
maxAge: expect.any(Number),
httpOnly: true,
sameSite: 'lax',
secure: false,
}),
);
expect(externalHooks.run).toHaveBeenCalledWith('user.profile.update', [
user.email,
@ -175,10 +181,16 @@ describe('MeController', () => {
expect(req.user.password).not.toBe(passwordHash);
const cookieOptions = captor<CookieOptions>();
expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, 'new-signed-token', cookieOptions);
expect(cookieOptions.value.httpOnly).toBe(true);
expect(cookieOptions.value.sameSite).toBe('lax');
expect(res.cookie).toHaveBeenCalledWith(
AUTH_COOKIE_NAME,
'new-signed-token',
expect.objectContaining({
maxAge: expect.any(Number),
httpOnly: true,
sameSite: 'lax',
secure: false,
}),
);
expect(externalHooks.run).toHaveBeenCalledWith('user.password.update', [
req.user.email,