Skip to content

Commit

Permalink
Add partitioned cookies
Browse files Browse the repository at this point in the history
  • Loading branch information
rawleyfowler committed Aug 28, 2024
1 parent ecb44cf commit 97108db
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 10 deletions.
17 changes: 14 additions & 3 deletions lib/Mojo/Cookie/Response.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use Mojo::Base 'Mojo::Cookie';
use Mojo::Date;
use Mojo::Util qw(quote split_cookie_header);

has [qw(domain expires host_only httponly max_age path samesite secure)];
has [qw(domain expires host_only httponly max_age partitioned path samesite secure)];

my %ATTRS = map { $_ => 1 } qw(domain expires httponly max-age path samesite secure);
my %ATTRS = map { $_ => 1 } qw(domain expires httponly max-age partitioned path samesite secure);

sub parse {
my ($self, $str) = @_;
Expand All @@ -21,7 +21,7 @@ sub parse {
next unless $ATTRS{my $attr = lc $name};
$value =~ s/^\.// if $attr eq 'domain' && defined $value;
$value = Mojo::Date->new($value // '')->epoch if $attr eq 'expires';
$value = 1 if $attr eq 'secure' || $attr eq 'httponly';
$value = 1 if $attr eq 'secure' || $attr eq 'httponly' || $attr eq 'partitioned';
$cookies[-1]{$attr eq 'max-age' ? 'max_age' : $attr} = $value;
}
}
Expand All @@ -47,6 +47,9 @@ sub to_string {
# "path"
if (my $path = $self->path) { $cookie .= "; path=$path" }

# "Partitioned"
$cookie .= "; Partitioned" if $self->partitioned;

# "secure"
$cookie .= "; secure" if $self->secure;

Expand Down Expand Up @@ -123,6 +126,14 @@ HttpOnly flag, which can prevent client-side scripts from accessing this cookie.
Max age for cookie.
=head2 partitioned
my $partitioned = $cookie->partitioned;
$cookie = $cookie->parititoned(1);
Partitioned flag, this is to be used in accordance to the L<CHIPS ammendment
to RFC 6265|https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-00.html>.
=head2 path
my $path = $cookie->path;
Expand Down
24 changes: 18 additions & 6 deletions lib/Mojolicious/Sessions.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ has cookie_name => 'mojolicious';
has cookie_path => '/';
has default_expiration => 3600;
has deserialize => sub { \&_deserialize };
has partitioned => 0;
has samesite => 'Lax';
has serialize => sub { \&_serialize };

Expand Down Expand Up @@ -51,12 +52,13 @@ sub store {
my $value = b64_encode $self->serialize->($session), '';
$value =~ y/=/-/;
my $options = {
domain => $self->cookie_domain,
expires => $session->{expires},
httponly => 1,
path => $self->cookie_path,
samesite => $self->samesite,
secure => $self->secure
domain => $self->cookie_domain,
expires => $session->{expires},
httponly => 1,
partitioned => $self->partitioned,
path => $self->cookie_path,
samesite => $self->samesite,
secure => $self->secure,
};
$c->signed_cookie($self->cookie_name, $value, $options);
}
Expand Down Expand Up @@ -143,6 +145,16 @@ A callback used to deserialize sessions, defaults to L<Mojo::JSON/"j">.
$sessions->deserialize(sub ($bytes) { return {} });
=head2 partitioned
my $bool = $sessions->partitioned;
$sessions = $sessions->partitioned($bool);
Set the partitioned flag on all session cookies, this is used in accordance to the L<CHIPS ammendment to
RFC 6265|https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-00.html>.
Partitioned cookies are held within a separate cookie jar per top-level site.
=head2 samesite
my $samesite = $sessions->samesite;
Expand Down
15 changes: 14 additions & 1 deletion t/mojo/cookie.t
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,14 @@ subtest 'Full response cookie as string' => sub {
$cookie->value('ba r');
$cookie->domain('example.com');
$cookie->path('/test');
$cookie->partitioned(1);
$cookie->max_age(60);
$cookie->expires(1218092879);
$cookie->secure(1);
$cookie->httponly(1);
$cookie->samesite('Lax');
is $cookie->to_string, '0="ba r"; expires=Thu, 07 Aug 2008 07:07:59 GMT; domain=example.com;'
. ' path=/test; secure; HttpOnly; SameSite=Lax; Max-Age=60', 'right format';
. ' path=/test; Partitioned; secure; HttpOnly; SameSite=Lax; Max-Age=60', 'right format';
};

subtest 'Empty response cookie' => sub {
Expand Down Expand Up @@ -216,6 +217,18 @@ subtest 'Parse response cookie (RFC 6265)' => sub {
is $cookies->[1], undef, 'no more cookies';
};

subtest 'Partitioned cookie (RFC 6265 CHIPS)' => sub {
my $cookies
= Mojo::Cookie::Response->parse(
'foo="bar"; Domain=example.com; Partitioned; Path=/test; Max-Age=60; Expires=Thu, 07 Aug 2008 07:07:59 GMT; Secure;'
);
is $cookies->[0]->partitioned, 1, 'partitioned set?';

$cookies = Mojo::Cookie::Response->parse(
'foo="bar"; Domain=example.com; Path=/test; Max-Age=60; Expires=Thu, 07 Aug 2008 07:07:59 GMT; Secure;');
is $cookies->[0]->partitioned, undef, 'partitioned not set?';
};

subtest 'Parse response cookie with invalid flag (RFC 6265)' => sub {
my $cookies = Mojo::Cookie::Response->parse(
'foo="ba r"; Domain=.example.com; Path=/test; Max-Age=60;' . ' Expires=Thu, 07 Aug 2008 07:07:59 GMT; InSecure;');
Expand Down

0 comments on commit 97108db

Please sign in to comment.