/meteor-cookies

🍪 Isomorphic bulletproof cookie functions for client and server

Primary LanguageJavaScriptBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

support support

Isomorphic Cookies

Isomorphic and bulletproof 🍪 cookies for meteor.js applications with support of Client, Server, Browser, Cordova, Meteor-Desktop, and other Meteor-supported environments.

  • 👨‍💻 Stable codebase, 320.000+ downloads;
  • 👨‍🔬 ~96% Tests coverage;
  • 📦 No external dependencies, no underscore, no jQuery, no Blaze;
  • 🖥 Full support with same API across Server and Client environments;
  • 📱 Compatible with Cordova, Browser, Meteor-Desktop, and other Meteor's Client environments;
  • ㊗️ Unicode support as cookies' value;
  • 👨‍💻 String, Array, Object, and Boolean are supported cookies' value types;
  • ♿ IE support, thanks to @derwok;
  • 📦 Looking for Client's (Browser) persistent storage? Try ClientStorage package.

Install:

meteor add ostrio:cookies

ES6 Import:

import { Cookies } from 'meteor/ostrio:cookies';

FAQ:

  • Cordova compatible? Cordova usageThis recommendation is only for outgoing Client -to-> Server Cookies; Server -to-> Client cookies work out-of-the-box. Enable withCredentials. Enable {allowQueryStringCookies: true} and {allowedCordovaOrigins: true} on both Client and Server. When those two options are set to true Cookies going to be transferred to server via get-query. As security measure only when Origin header matches ^http://localhost:12[0-9]{3}$ expression. Meteor/Cordova connect through localhost:12XXX, local server, for outgoing requests, this also instructs the server to respond with the requested cookies (sent as GET-Parameters) in the response as Set-Cookie header. The reason for this workaround is the general lack of cookie support in Meteor/Cordova when setting in the client — but cookies set by the server are always sent along with every request;
  • Cookies are missing on Server — In 99% cases it's caused by Meteor's webapp http server callback-chain disorder. Make sure new Cookies() is called before Routes are registered. Routing packages usually take care of * (e.g. catch-all or 404) route, not passing request further to callback-chain. And as freshly installed package it would be placed at the end of .meteor/packages file, where list-order matters. We recommend to place ostrio:cookies package above all community packages in .meteor/packages list;
  • Meteor-Desktop compatibility: meteor-cookies can be used in meteor-desktop projects as well. As meteor-desktop works exactly like Cordova, all Cordova requirements and recommendations apply.

API:

  • Note — On a server, cookies will be set only after headers are sent (on next route or page reload). To send cookies from Client to Server without a page reload use send() method.
  • Server Usage Note — On a server Cookies implemented as a middleware. To get access to current user's cookies use req.Cookies instance. For more - see examples section below.

Fetch cookies new Cookies(opts) [Isomorphic]

Create new instance of Cookies

  • opts.auto {Boolean} - [Server] Auto-bind in middleware as req.Cookies, by default true
  • opts.handler {Function} - [Server] Middleware function (e.g. hook/callback called within middleware pipeline) with single argument cookies as Cookies instance. See "Alternative Usage" section
  • opts.onCookies {Function} - [Server] Callback/hook triggered after .send() method called on Client and received by Server, called with single argument cookies as Cookies instance. Note: this hook available only if auto option is true
  • opts.TTL {Number|Boolean} - Default cookies expiration time (max-age) in milliseconds, by default - false (session, no TTL)
  • opts.runOnServer {Boolean} - Set to false to avoid server usage (by default - true)
  • opts.allowQueryStringCookies {Boolean} - Allow passing Cookies in a query string (in URL). Primary should be used only in Cordova environment. Note: this option will be used only on Cordova
  • opts.allowedCordovaOrigins {Regex|Boolean} - [Server] Allow setting Cookies from that specific origin which in Meteor/Cordova is localhost:12XXX. Set to default ^http:\/\/localhost:12[0-9]{3}$ if set to true. Default: false
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();

cookies.get(key) [Isomorphic]

Read a cookie. If the cookie doesn't exist a null will be returned.

  • key {String} - The name of the cookie to read

cookies.set(key, value, [opts]) [Isomorphic]

Create/overwrite a cookie.

  • key {String} - The name of the cookie to create/overwrite
  • value {String|Number|Boolean|Object|Array} - The value of the cookie
  • opts {Object} - [Optional]
  • opts.expires {Number|Date|Infinity} - [Optional] Date, Number as milliseconds or Infinity for a never-expires cookie. If not specified the cookie will expire at the end of session (number as milliseconds or Date object)
  • opts.maxAge {Number} - [Optional] The max-age in seconds (e.g. 31536e3 for a year)
  • opts.path {String} - [Optional] The path from where the cookie will be readable. E.g., "/", "/mydir"; if not specified, defaults to the current path of the current document location (string or null). The path must be absolute (see RFC 2965). For more information on how to use relative paths in this argument, see: docs
  • opts.domain {String} - [Optional] The domain from where the cookie will be readable. E.g., "example.com", ".example.com" (includes all subdomains) or "subdomain.example.com"; if not specified, defaults to the host portion of the current document location (string or null)
  • opts.secure {Boolean} - [Optional] The cookie will be transmitted only over secure protocol as https
  • opts.httpOnly {Boolean} - [Optional] An HttpOnly cookie cannot be accessed by client-side APIs, such as JavaScript. This restriction eliminates the threat of cookie theft via cross-site scripting (XSS)
  • opts.sameSite {Boolean} {String: None, Strict, or Lax} - [Optional] Cross-site cookies usage policy. Read more on wikipedia, web.dev, and ietf. Default: false
  • opts.firstPartyOnly {Boolean} - [Optional] Deprecated use sameSite instead

cookies.remove([key], [path], [domain]) [Isomorphic]

  • remove() - Remove all cookies on current domain
  • remove(key) - Remove a cookie on current domain
  • remove(key, path, domain):
    • key {String} - The name of the cookie to create/overwrite
    • path {String} - [Optional] The path from where the cookie was readable. E.g., "/", "/mydir"; if not specified, defaults to the current path of the current document location (string or null). The path must be absolute (see RFC 2965). For more information on how to use relative paths in this argument, read more
    • domain {String} - [Optional] The domain from where the cookie was readable. E.g., "example.com", ".example.com" (includes all subdomains) or "subdomain.example.com"; if not specified, defaults to the host portion of the current document location (string or null)

cookies.has(key) [Isomorphic]

Check whether a cookie exists in the current position, returns boolean value

  • key {String} - The name of the cookie to check

cookies.keys() [Isomorphic]

Returns an array of all readable cookies from this location

cookies.send([callback]) [Client]

Send all current cookies to server.

Examples:

/* Both Client & Server */
import { Meteor } from 'meteor/meteor';
import { Cookies } from 'meteor/ostrio:cookies';
const cookies = new Cookies();

/* Client */
if (Meteor.isClient) {
  cookies.set('locale', 'en'); //true
  cookies.set('country', 'usa'); //true
  cookies.set('gender', 'male'); //true

  cookies.get('gender'); //male

  cookies.has('locale'); //true
  cookies.has('city'); //false

  cookies.keys(); //['locale', 'country', 'gender']

  cookies.remove('locale'); //true
  cookies.get('locale'); //undefined

  cookies.keys(); //['country', 'gender']

  cookies.remove(); //true
  cookies.keys(); //[""]

  cookies.remove(); //false
}

/* Server */
if (Meteor.isServer) {
  const { WebApp } = require('meteor/webapp');

  WebApp.connectHandlers.use((req, res, next) => {
    cookies = req.Cookies;

    cookies.set('locale', 'en'); //true
    cookies.set('country', 'usa'); //true
    cookies.set('gender', 'male'); //true

    cookies.get('gender'); //male

    cookies.has('locale'); //true
    cookies.has('city'); //false

    cookies.keys(); //['locale', 'country', 'gender']

    cookies.remove('locale'); //true
    cookies.get('locale'); //undefined

    cookies.keys(); //['country', 'gender']

    cookies.remove(); //true
    cookies.keys(); //[""]

    cookies.remove(); //false

    next(); // Pass request to the next handler
  });
}

Alternative Usage

/* Both Client & Server */
import { Meteor } from 'meteor/meteor';
import { Cookies } from 'meteor/ostrio:cookies';

/* Client */
if (Meteor.isClient) {
  const cookies = new Cookies();
  cookies.set('gender', 'male'); //true
  cookies.get('gender'); //male
  cookies.has('city'); //false
  cookies.keys(); //['gender']
}

/* Server */
if (Meteor.isServer) {
  const { WebApp } = require('meteor/webapp');

  const cookie = new Cookies({
    auto: false, // Do not bind as a middleware by default (recommended, but not required)
    handler(cookies) {
      cookies.set('gender', 'male'); //true
      cookies.get('gender'); //male
      cookies.has('city'); //false
      cookies.keys(); //['gender']
    }
  });

  WebApp.connectHandlers.use(cookie.middleware());
}

Running Tests

  1. Clone this package
  2. In Terminal (Console) go to directory where package is cloned
  3. Then run:

Meteor/Tinytest

# Default
meteor test-packages ./

# With custom port
meteor test-packages ./ --port 8888

Support our open source contributions