Skip to main content
Version: Next

Cookies

Handlers are able to interact with a cookies store that you can use to store small amounts of data - called cookies - on the client. This data will be persisted across requests and will be made accessible with every incoming request.

Basic usage

Cookies can be interacted with by leveraging a cookie store: an instance of Marten::HTTP::Cookies that provides a hash-like interface allowing to retrieve and store cookie values. This cookie store can be accessed from three different places:

Here is a very simple example of how to interact with the cookies store within a handler:

class MyHandler < Marten::Handler
def get
cookies[:foo] = "bar"
respond "Hello World!"
end
end

The most simple way to retrieve the value of a cookie is to leverage the #[] method or one of its variants.

For example, the following lines could be used to read the value of a cookie named foo:

request.cookies[:foo]  # => returns the value of "foo" or raises a KeyError if not found
request.cookies[:foo]? # => returns the value of "foo" or returns nil if not found

Alternatively, the #fetch method can also be leveraged in order to execute a block or return a default value if the specified cookie is not found:

request.cookies.fetch(:foo, "defaultval")
request.cookies.fetch(:foo) { "defaultval" }

Setting cookies

The most simple way to set a new cookie is to call the #[]= method on a cookie store. For example:

request.cookies[:foo] = "bar"

Calling this method will create a new cookie with the specified name and value. It should be noted that cookies created with the #[]= method will not expire, will be associated with the root path (/), and will not be secure.

Alternatively, it is possible to leverage the #set in order to specify custom cookie properties while setting new cookie values. For example:

request.cookies.set(
:foo,
"bar",
expires: 2.days.from_now,
secure: true,
same_site: "lax"
)

Appart from the cookie name and value, the #set method allows to define some additional cookie properties:

  • The cookie expiry datetime (expires argument).
  • The cookie path.
  • The associated domain (useful in order to define cross-domain cookies).
  • Whether or not the cookie should be sent for HTTPS requests only (secure argument).
  • Whether or not client-side scripts should have access to the cookie (http_only argument).
  • The same_site policy (accepted values are "lax" or "strict").

Deleting cookies

Cookies can be deleted by leveraging the #delete method. This method will delete a specific cookie and return its value, or nil if the cookie does not exist:

request.cookies.delete(:foo)

Apart from the name of the cookie, this method allows to define some additional properties of the cookie to delete:

  • The cookie path.
  • The associated domain (useful in order to define cross-domain cookies).
  • The same_site policy (accepted values are "lax" or "strict").

Note that the path, domain, and same_site values should always be the same as the ones that were used to create the cookie in the first place. Otherwise, the cookie might not be deleted properly.

Signed cookies

In addition to the regular cookie store, Marten provides a signed cookie store version (which is accessible through the use of the Marten::HTTP::Cookies#signed method) where cookies are signed (but not encrypted). This means that whenever a cookie is requested from this store, the signed representation of the corresponding value will be verified. This is useful to create cookies that can't be tampered by users, but it should be noted that the actual data can still be read by the client technically.

All the methods that can be used with the regular cookie store that were highlighted in Basic usage can also be used with the signed cookie store:

# Retrieving cookies...
request.signed.cookies[:foo]
request.signed.cookies[:foo]?
request.signed.cookies.fetch(:foo, "defaultval")
request.signed.cookies.fetch(:foo) { "defaultval" }

# Setting cookies...
request.signed.cookies[:foo] = "bar"
request.signed.cookies.set(:foo, "bar", expires: 2.days.from_now)

# Deleting cookies...
request.signed.cookies.delete(:foo)

The signed cookie store uses a Marten::Core::Signer signer object in order to sign cookie values and to verify the signature of retrieved cookies. This means that cookies are signed with HMAC signatures that use the SHA256 hash algorithm.

info

Only cookie values are signed. Cookie names are not signed.

Encrypted cookies

In addition to the regular cookie store, Marten provides an encrypted cookie store version (which is accessible through the use of the Marten::HTTP::Cookies#encrypted method) where cookies are signed and encrypted. This means that whenever a cookie is requested from this store, the raw value of the cookie will be decrypted and its signature will be verified. This is useful to create cookies whose values can't be read nor tampered by users.

All the methods that can be used with the regular cookie store that were highlighted in Basic usage can also be used with the encrypted cookie store:

# Retrieving cookies...
request.encrypted.cookies[:foo]
request.encrypted.cookies[:foo]?
request.encrypted.cookies.fetch(:foo, "defaultval")
request.encrypted.cookies.fetch(:foo) { "defaultval" }

# Setting cookies...
request.encrypted.cookies[:foo] = "bar"
request.encrypted.cookies.set(:foo, "bar", expires: 2.days.from_now)

# Deleting cookies...
request.encrypted.cookies.delete(:foo)

The signed cookie store uses a Marten::Core::Encryptor encryptor object in order to encrypt and sign cookie values. This means that cookies are:

  • encrypted with an aes-256-cbc cipher.
  • signed with HMAC signatures that use the SHA256 hash algorithm.
info

Only cookie values are encrypted and signed. Cookie names are not encrypted.