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
Accessing the cookie store
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:
- Handlers can access it through the use of the
#cookies
method. Marten::HTTP::Request
objects give access to the cookies associated with the request via the#cookies
method.Marten::HTTP::Response
objects give access to the cookies that will be returned with the HTTP response via the#cookies
method.
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
Retrieving cookie values
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.
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.
Only cookie values are encrypted and signed. Cookie names are not encrypted.