├── LICENSE.txt ├── README.markdown ├── example └── ex1.js ├── index.js └── package.json /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Jed Schmidt, http://jedschmidt.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | cookie-node.js 2 | ============ 3 | 4 | # NOTE: This library has been deprecated. 5 | 6 | Please use [Cookies](https://github.com/jed/cookies) instead. Not only is it built on the latest version of node.js without any cruft from older versions, but the signing mechanism has been factored out into [Keygrip](/jed/keygrip), a more flexible and performant library. 7 | 8 | `cookie-node` is a cookie module for [node.js](http://nodejs.org/), based 9 | loosely on Tornado's approach to [signed cookies](http://www.tornadoweb.org/documentation#cookies-and-secure-cookies). 10 | 11 | To start, require the library in your app: 12 | 13 | var cookie = require( "./cookie-node" ); 14 | 15 | This extends the `ServerRequest` and `ServerResponse` objects, allowing you to 16 | get cookies on requests and set them on responses for server calls: 17 | 18 | function( req, res ) { 19 | var name = req.getCookie( "name" ), 20 | length = name.length; 21 | 22 | res.setCookie( "name_length", length ); 23 | 24 | res.writeHead(200, {"Content-Type": "text/html"}); 25 | res.write( "Your name has " + length + " characters." ); 26 | res.close(); 27 | } 28 | 29 | You can also set a cookie secret to enable signed cookies, and prevent forged 30 | cookies: 31 | 32 | cookie.secret = "myRandomSecretThatNoOneWillGuess"; 33 | 34 | so that the above becomes: 35 | 36 | function( req, res ) { 37 | var name = req.getSecureCookie( "name" ), 38 | length = name.length; 39 | 40 | res.setSecureCookie( "name_length", length ); 41 | 42 | res.writeHead(200, {"Content-Type": "text/html"}); 43 | res.write( "Your name has " + length + " characters." ); 44 | res.close(); 45 | } 46 | 47 | (You don't need to set the secret, but your cookies will end up being 48 | invalidated when the server restarts, and you will be yelled at.) 49 | 50 | When you set a secure cookie, the value is stored alongside its expiration 51 | date, as well as an HMAC SHA-1 digest of the two values with your secret. If a 52 | cookie's signature does not match that calculated on the server, the 53 | `getSecureCookie` method throws. 54 | 55 | If you'd like to clear a cookie, just use `res.clearCookie( name )`. 56 | 57 | That's about it. Send any questions or comments [here](http://twitter.com/jedschmidt). 58 | -------------------------------------------------------------------------------- /example/ex1.js: -------------------------------------------------------------------------------- 1 | var http = require('http'), 2 | cookie = require('../'); 3 | 4 | cookie.secret = "50m3thing-l0ng-@nd-r@nd0m-th@t-n0-0n3-will-gu355"; 5 | 6 | http.createServer(function (req, res) { 7 | var rdm = req.getSecureCookie("rdm"); 8 | if (!rdm) res.setSecureCookie("rdm", (Math.random()*1e9).toString(36)); 9 | res.writeHead(200, {"Content-Type": "text/html"}); 10 | if (rdm) res.write("Cookie has value: " + rdm); 11 | res.end(); 12 | }).listen(8080); 13 | 14 | console.log('Server running at http://127.0.0.1:8080/'); 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | console.warn( 2 | "This library has been deprecated.", 3 | "Please use `npm install cookies` instead." 4 | ) 5 | 6 | var crypto = require('crypto'); 7 | var Buffer = require('buffer').Buffer; 8 | 9 | function hex_hmac_sha1(data, key) { 10 | var hmac = crypto.createHmac('sha1', key); 11 | hmac.update(data); 12 | return hmac.digest('hex'); 13 | } 14 | 15 | var base64 = { 16 | encode: function(str) { 17 | return (new Buffer(str)).toString('base64'); 18 | }, 19 | decode: function(str) { 20 | return (new Buffer(str, 'base64')).toString('utf8'); 21 | } 22 | }; 23 | 24 | var processCookie = exports.processCookie = function(name, value) { 25 | var len, parts, expires, remoteSig, localSig; 26 | 27 | parts = value.replace(/\*/g, '=').split("|"); 28 | 29 | if ( parts.length !== 4 ) { 30 | return null; 31 | } 32 | 33 | len = parts[0]; 34 | value = base64.decode( parts[1] ).substr(0, len); 35 | expires = new Date( +parts[2] ); 36 | remoteSig = parts[3]; 37 | 38 | if ( expires < Date.now() ) { 39 | return null; 40 | } 41 | 42 | localSig = hex_hmac_sha1( parts.slice( 0, 3 ).join("|"), cookieSecret() ); 43 | 44 | if ( localSig !== remoteSig ) { 45 | throw new Error("invalid cookie signature: " + name); 46 | } 47 | 48 | return value; 49 | }; 50 | 51 | 52 | var mutateHttp = function(http){ 53 | http.IncomingMessage.prototype._parseCookies = function() { 54 | var header = this.headers["cookie"] || "", 55 | ret = {}; 56 | 57 | header.split(";").forEach( function( cookie ) { 58 | var parts = cookie.split("="), 59 | name = (parts[0] ? parts[0].trim() : ''), 60 | value = (parts[1] ? parts[1].trim() : ''); 61 | 62 | ret[ name ] = value; 63 | }); 64 | return this.cookies = ret; 65 | }; 66 | 67 | 68 | http.IncomingMessage.prototype.getCookie = function( name ) { 69 | var cookies = this.cookies || this._parseCookies(); 70 | return cookies[ name ] || null; 71 | }; 72 | 73 | http.IncomingMessage.prototype.getSecureCookie = function( name ) { 74 | var value = this.getCookie( name ); 75 | 76 | if ( !value ) { 77 | return null; 78 | } 79 | 80 | return processCookie(name, value); 81 | }; 82 | 83 | // this probably isn't kosher, but it's the best way to keep the interface sane. 84 | var _writeHead = http.ServerResponse.prototype.writeHead; 85 | var COOKIE_KEY = 'Set-Cookie', slice = Array.prototype.slice; 86 | http.ServerResponse.prototype.writeHead = function () { 87 | // Honor the passed args and method signature (see http.writeHead docs) 88 | var args = slice.call(arguments), headers = args[args.length-1]; 89 | if (!headers || typeof(headers) != 'object') { 90 | // No header arg - create and append to args list 91 | args.push(headers = []); 92 | } 93 | 94 | // Merge cookie values 95 | var prev = headers[COOKIE_KEY], cookies = this.cookies || []; 96 | if (prev) cookies.push(prev); 97 | if (cookies.length > 0) headers[COOKIE_KEY] = cookies.join(" "); 98 | 99 | // Invoke original writeHead() 100 | _writeHead.apply(this, args); 101 | }; 102 | 103 | http.ServerResponse.prototype.setCookie = function( name, value, options ) { 104 | var cookies = this.cookies || ( this.cookies = [] ), 105 | cookie = [ name, "=", value, ";" ]; 106 | 107 | options = options || {}; 108 | 109 | if ( options.expires ) 110 | cookie.push( " expires=", (new Date(options.expires)).toUTCString(), ";" ); 111 | 112 | if ( options.path ) 113 | cookie.push( " path=", options.path, ";" ); 114 | 115 | if ( options.domain ) 116 | cookie.push( " domain=", options.domain, ";" ); 117 | 118 | if ( options.secure ) 119 | cookie.push( " secure", ";" ); 120 | 121 | if ( options.httpOnly ) 122 | cookie.push( " httponly" ); 123 | 124 | cookies.push( cookie.join("") ); 125 | }; 126 | 127 | http.ServerResponse.prototype.generateCookieValue = function( value, options ) { 128 | options = options || {}; 129 | value = [ (value + '').length, base64.encode( value ), +options.expires ]; 130 | var signature = hex_hmac_sha1( value.join("|"), cookieSecret() ); 131 | 132 | value.push( signature ); 133 | value = value.join("|").replace(/=/g, '*'); 134 | 135 | return value; 136 | }; 137 | 138 | http.ServerResponse.prototype.setSecureCookie = function( name, value, options ) { 139 | options = options || {}; 140 | if (value !== null && typeof value !== 'undefined') value = value.toString(); 141 | else value = ''; 142 | value = this.generateCookieValue(value, options); 143 | this.setCookie( name, value, options ); 144 | }; 145 | 146 | http.ServerResponse.prototype.clearCookie = function( name, options ) { 147 | options = options || {}; 148 | options.expires = new Date( Date.now() - 30 * 24 * 60 * 60 * 1000 ); 149 | this.setCookie( name, "", options ); 150 | }; 151 | }; 152 | 153 | mutateHttp(require('http')); 154 | 155 | 156 | function cookieSecret() { 157 | if ( exports.secret ) 158 | return exports.secret; 159 | 160 | return exports.secret = hex_hmac_sha1( Math.random(), Math.random() ); 161 | } 162 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name" : "cookie" 2 | , "version" : "0.1.5" 3 | , "description" : "A deprecated node.js library for handling secure cookies." 4 | , "main" : "./index" 5 | , "engines": [ "node" ] 6 | } 7 | --------------------------------------------------------------------------------