Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export interface StateOptions<HapiRequest> {
*/
isSameSite?: SameSitePolicy | undefined;

/**
* Sets the `Partitioned` flag. For more information, please access https://developers.google.com/privacy-sandbox/3pcd/chips
*/
isPartitioned?: boolean | undefined;

/**
* The path scope.
*
Expand Down
14 changes: 14 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internals.schema = Validate.object({
ignoreErrors: Validate.boolean(),
isSecure: Validate.boolean(),
isHttpOnly: Validate.boolean(),
isPartitioned: Validate.boolean(),
isSameSite: Validate.valid('Strict', 'Lax', 'None', false),
path: Validate.string().allow(null),
domain: Validate.string().allow(null),
Expand All @@ -47,6 +48,7 @@ internals.defaults = {
ignoreErrors: false,
isSecure: true,
isHttpOnly: true,
isPartitioned: false,
isSameSite: 'Strict',
path: null,
domain: null,
Expand Down Expand Up @@ -293,6 +295,18 @@ exports.Definitions = class {
segment = `${segment}; SameSite=${definition.isSameSite}`;
}

if (definition.isPartitioned) {
if (!definition.isSecure) {
throw Boom.badImplementation('Partitioned cookies must be secure');
}

if (definition.isSameSite !== 'None') {
throw Boom.badImplementation('Partitioned cookies must have SameSite=None');
}

segment = `${segment}; Partitioned`;
}

if (definition.domain) {
const domain = definition.domain.toLowerCase();
if (!domain.match(internals.validateRx.domainLabelLenRx)) {
Expand Down
44 changes: 44 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -354,6 +355,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -381,6 +383,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -408,6 +411,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand All @@ -424,6 +428,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand All @@ -447,6 +452,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -478,6 +484,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand All @@ -502,6 +509,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand All @@ -518,6 +526,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -553,6 +562,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand All @@ -576,6 +586,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -603,6 +614,7 @@ describe('Definitions', () => {
isSecure: true,
isHttpOnly: true,
isSameSite: 'Strict',
isPartitioned: false,
path: null,
domain: null,
ttl: null,
Expand Down Expand Up @@ -1034,6 +1046,38 @@ describe('Definitions', () => {
const definitions = new Statehood.Definitions();
await expect(definitions.format({ name: 'sid', value: 'fihfieuhr9384hf', options: { isHttpOnly: false, path: 'd', domain: 'example.com' } })).to.reject('Invalid cookie path: d');
});

describe('with partitioned cookies', () => {

it('formats a header with cookie partitioned', async () => {

const definitions = new Statehood.Definitions();
const header = await definitions.format({ name: 'sid', value: 'fihfieuhr9384hf', options: { isPartitioned: true, isSecure: true, isHttpOnly: true, isSameSite: 'None' } });
expect(header[0]).to.equal('sid=fihfieuhr9384hf; Secure; HttpOnly; SameSite=None; Partitioned');
});

it('throws error if partitioned option if not secure', async () => {

const definitions = new Statehood.Definitions();
const err = await expect(definitions.format({ name: 'sid', value: 'fihfieuhr9384hf', options: { isPartitioned: true, isSecure: false, isHttpOnly: true, isSameSite: 'None' } })).to.reject();
expect(err.message).to.equal('Partitioned cookies must be secure');
});

it('throws error if partitioned option if not SameSite=None', async () => {

const definitions = new Statehood.Definitions();
const err = await expect(definitions.format({ name: 'sid', value: 'fihfieuhr9384hf', options: { isPartitioned: true, isSecure: true, isHttpOnly: true, isSameSite: 'Lax' } })).to.reject();
expect(err.message).to.equal('Partitioned cookies must have SameSite=None');
});

it('throws error if partitioned option if not secure and not SameSite=None', async () => {

const definitions = new Statehood.Definitions();
const err = await expect(definitions.format({ name: 'sid', value: 'fihfieuhr9384hf', options: { isPartitioned: true, isSecure: false, isHttpOnly: true, isSameSite: 'Lax' } })).to.reject();
expect(err.message).to.equal('Partitioned cookies must be secure');
});
});

});

describe('passThrough()', () => {
Expand Down
Loading