Skip to content

Cookies

MVC::Keayl::Cookies is the cookie jar. A controller exposes it as cookies, built from the request's Cookie header and flushed to the response as Set-Cookie headers after the action runs.

1
2
3
self.cookies<theme>;            # read an incoming cookie
self.cookies<theme> = 'dark';   # write a cookie
self.cookies.delete('theme');   # delete a cookie

Attributes

set takes the cookie attributes: path, domain, expires (a string or a DateTime, formatted as an HTTP date), max-age (seconds), secure, http-only, and same-site:

1
self.cookies.set('session', $id, path => '/', http-only => True, same-site => 'Lax', secure => True);

Assigning a hash sets the value alongside its options:

1
self.cookies<session> = { value => $id, path => '/admin' };

delete writes a cookie with a past expiry and Max-Age=0. Values are url-encoded on the way out and url-decoded on the way in.

Signed cookies

cookies.signed is a tamper-evident jar. A written value gets an HMAC-SHA1 signature using the controller's secret; reading verifies it with a constant-time comparison and returns the value, or undefined if the signature does not match (tampering or a different secret):

1
2
self.cookies.signed<user-id> = $user.id;
my $id = self.cookies.signed<user-id>;   # the value, or Nil if tampered

Encrypted cookies

cookies.encrypted keeps the value confidential. It encrypts with AES-256-CBC under a key derived from the secret, then authenticates the ciphertext with an HMAC (encrypt-then-MAC), so a wrong secret or tampered value fails to decrypt rather than returning garbage:

1
2
self.cookies.encrypted<card> = $number;
my $number = self.cookies.encrypted<card>;   # the value, or Nil if it cannot be authenticated

The signing and encryption keys come from the controller's secret, so set a strong secret for any application that uses signed or encrypted cookies.