Skip to content

Commit

Permalink
Add "sameSite" option for SameSite cookie support
Browse files Browse the repository at this point in the history
closes #75
closes #80
  • Loading branch information
dougwilson committed Jan 29, 2017
1 parent 73d4ba8 commit 24fef5c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 0 deletions.
5 changes: 5 additions & 0 deletions History.md
@@ -1,3 +1,8 @@
unreleased
==========

* Add `sameSite` option for SameSite cookie support

0.6.2 / 2016-11-12
==================

Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -71,6 +71,7 @@ If the _options_ object is provided, it will be used to generate the outbound co
* `domain`: a string indicating the domain of the cookie (no default).
* `secure`: a boolean indicating whether the cookie is only to be sent over HTTPS (`false` by default for HTTP, `true` by default for HTTPS). [Read more about this option below](#secure-cookies).
* `httpOnly`: a boolean indicating whether the cookie is only to be sent over HTTP(S), and not made available to client JavaScript (`true` by default).
* `sameSite`: a boolean or string indicating whether the cookie is a "same site" cookie (`false` by default). This can be set to `'strict'`, `'lax'`, or `true` (which maps to `'strict'`).
* `signed`: a boolean indicating whether the cookie is to be signed (`false` by default). If this is true, another cookie of the same name with the `.sig` suffix appended will also be sent, with a 27-byte url-safe base64 SHA1 value representing the hash of _cookie-name_=_cookie-value_ against the first [Keygrip](https://www.npmjs.com/package/keygrip) key. This signature key is used to detect tampering the next time a cookie is received.
* `overwrite`: a boolean indicating whether to overwrite previously set cookies of the same name (`false` by default). If this is true, all cookies set during the same request with the same name (regardless of path or domain) are filtered out of the Set-Cookie header when setting this cookie.

Expand Down
12 changes: 12 additions & 0 deletions lib/cookies.js
Expand Up @@ -20,6 +20,12 @@ var cache = {}

var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;

/**
* RegExp to match Same-Site cookie attribute value.
*/

var sameSiteRegExp = /^(?:lax|strict)$/i

function Cookies(request, response, options) {
if (!(this instanceof Cookies)) return new Cookies(request, response, options)

Expand Down Expand Up @@ -133,12 +139,17 @@ function Cookie(name, value, attrs) {
if (this.domain && !fieldContentRegExp.test(this.domain)) {
throw new TypeError('option domain is invalid');
}

if (this.sameSite && this.sameSite !== true && !sameSiteRegExp.test(this.sameSite)) {
throw new TypeError('option sameSite is invalid')
}
}

Cookie.prototype.path = "/";
Cookie.prototype.expires = undefined;
Cookie.prototype.domain = undefined;
Cookie.prototype.httpOnly = true;
Cookie.prototype.sameSite = false;
Cookie.prototype.secure = false;
Cookie.prototype.overwrite = false;

Expand All @@ -154,6 +165,7 @@ Cookie.prototype.toHeader = function() {
if (this.path ) header += "; path=" + this.path
if (this.expires ) header += "; expires=" + this.expires.toUTCString()
if (this.domain ) header += "; domain=" + this.domain
if (this.sameSite ) header += "; samesite=" + (this.sameSite === true ? 'strict' : this.sameSite.toLowerCase())
if (this.secure ) header += "; secure"
if (this.httpOnly ) header += "; httponly"

Expand Down
53 changes: 53 additions & 0 deletions test/cookie.js
Expand Up @@ -56,5 +56,58 @@ describe('new Cookie(name, value, [options])', function () {
assert.equal(cookie.maxage, 86400)
})
})

describe('sameSite', function () {
it('should set the .sameSite property', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: true })
assert.equal(cookie.sameSite, true)
})

it('should default to false', function () {
var cookie = new cookies.Cookie('foo', 'bar')
assert.equal(cookie.sameSite, false)
})

it('should throw on invalid value', function () {
assert.throws(function () {
new cookies.Cookie('foo', 'bar', { sameSite: 'foo' })
}, /option sameSite is invalid/)
})

describe('when set to "false"', function () {
it('should not set "samesite" attribute in header', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: false })
assert.equal(cookie.toHeader(), 'foo=bar; path=/; httponly')
})
})

describe('when set to "true"', function () {
it('should set "samesite=strict" attribute in header', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: true })
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly')
})
})

describe('when set to "lax"', function () {
it('should set "samesite=lax" attribute in header', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: 'lax' })
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=lax; httponly')
})
})

describe('when set to "strict"', function () {
it('should set "samesite=strict" attribute in header', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: 'strict' })
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly')
})
})

describe('when set to "STRICT"', function () {
it('should set "samesite=strict" attribute in header', function () {
var cookie = new cookies.Cookie('foo', 'bar', { sameSite: 'STRICT' })
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly')
})
})
})
})
})

0 comments on commit 24fef5c

Please sign in to comment.