Skip to content

Commit

Permalink
feat(core): Set the secure flag on issued cookies
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy committed Mar 5, 2024
1 parent 62282c2 commit b1f0546
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 12 deletions.
10 changes: 10 additions & 0 deletions packages/cli/BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class AuthService {
maxAge: this.jwtExpiration * Time.seconds.toMilliseconds,
httpOnly: true,
sameSite: 'lax',
secure: config.getEnv('secure_cookie'),
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/test/unit/auth/auth.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ describe('AuthService', () => {
httpOnly: true,
maxAge: 604800000,
sameSite: 'lax',
secure: false,
});
});
});
Expand Down Expand Up @@ -177,6 +178,7 @@ describe('AuthService', () => {
httpOnly: true,
maxAge: 604800000,
sameSite: 'lax',
secure: false,
});
});

Expand Down
36 changes: 24 additions & 12 deletions packages/cli/test/unit/controllers/me.controller.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit b1f0546

Please sign in to comment.