Skip to content

Commit 5e06940

Browse files
committed
Add partitioned cookies
1 parent ecb44cf commit 5e06940

File tree

3 files changed

+46
-11
lines changed

3 files changed

+46
-11
lines changed

lib/Mojo/Cookie/Response.pm

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use Mojo::Base 'Mojo::Cookie';
44
use Mojo::Date;
55
use Mojo::Util qw(quote split_cookie_header);
66

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

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

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

50+
# "Partitioned"
51+
$cookie .= "; Partitioned" if $self->partitioned;
52+
5053
# "secure"
5154
$cookie .= "; secure" if $self->secure;
5255

@@ -123,6 +126,14 @@ HttpOnly flag, which can prevent client-side scripts from accessing this cookie.
123126
124127
Max age for cookie.
125128
129+
=head2 partitioned
130+
131+
my $partitioned = $cookie->partitioned;
132+
$cookie = $cookie->parititoned(1);
133+
134+
Partitioned flag, this is to be used in accordance to the L<CHIPS ammendment
135+
to RFC 6265|https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-00.html>.
136+
126137
=head2 path
127138
128139
my $path = $cookie->path;

lib/Mojolicious/Sessions.pm

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use Mojo::Base -base;
44
use Mojo::JSON;
55
use Mojo::Util qw(b64_decode b64_encode);
66

7-
has [qw(cookie_domain secure)];
7+
has [qw(cookie_domain partitioned secure)];
88
has cookie_name => 'mojolicious';
99
has cookie_path => '/';
1010
has default_expiration => 3600;
@@ -51,12 +51,13 @@ sub store {
5151
my $value = b64_encode $self->serialize->($session), '';
5252
$value =~ y/=/-/;
5353
my $options = {
54-
domain => $self->cookie_domain,
55-
expires => $session->{expires},
56-
httponly => 1,
57-
path => $self->cookie_path,
58-
samesite => $self->samesite,
59-
secure => $self->secure
54+
domain => $self->cookie_domain,
55+
expires => $session->{expires},
56+
httponly => 1,
57+
partitioned => $self->partitioned,
58+
path => $self->cookie_path,
59+
samesite => $self->samesite,
60+
secure => $self->secure,
6061
};
6162
$c->signed_cookie($self->cookie_name, $value, $options);
6263
}
@@ -143,6 +144,16 @@ A callback used to deserialize sessions, defaults to L<Mojo::JSON/"j">.
143144
144145
$sessions->deserialize(sub ($bytes) { return {} });
145146
147+
=head2 partitioned
148+
149+
my $bool = $sessions->partitioned;
150+
$sessions = $sessions->partitioned($bool);
151+
152+
Set the partitioned flag on all session cookies, this is used in accordance to the L<CHIPS ammendment to
153+
RFC 6265|https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-00.html>.
154+
155+
Partitioned cookies are held within a separate cookie jar per top-level site.
156+
146157
=head2 samesite
147158
148159
my $samesite = $sessions->samesite;

t/mojo/cookie.t

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,14 @@ subtest 'Full response cookie as string' => sub {
168168
$cookie->value('ba r');
169169
$cookie->domain('example.com');
170170
$cookie->path('/test');
171+
$cookie->partitioned(1);
171172
$cookie->max_age(60);
172173
$cookie->expires(1218092879);
173174
$cookie->secure(1);
174175
$cookie->httponly(1);
175176
$cookie->samesite('Lax');
176177
is $cookie->to_string, '0="ba r"; expires=Thu, 07 Aug 2008 07:07:59 GMT; domain=example.com;'
177-
. ' path=/test; secure; HttpOnly; SameSite=Lax; Max-Age=60', 'right format';
178+
. ' path=/test; Partitioned; secure; HttpOnly; SameSite=Lax; Max-Age=60', 'right format';
178179
};
179180

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

220+
subtest 'Partitioned cookie (RFC 6265 CHIPS)' => sub {
221+
my $cookies
222+
= Mojo::Cookie::Response->parse(
223+
'foo="bar"; Domain=example.com; Partitioned; Path=/test; Max-Age=60; Expires=Thu, 07 Aug 2008 07:07:59 GMT; Secure;'
224+
);
225+
is $cookies->[0]->partitioned, 1, 'partitioned set?';
226+
227+
$cookies = Mojo::Cookie::Response->parse(
228+
'foo="bar"; Domain=example.com; Path=/test; Max-Age=60; Expires=Thu, 07 Aug 2008 07:07:59 GMT; Secure;');
229+
is $cookies->[0]->partitioned, undef, 'partitioned not set?';
230+
};
231+
219232
subtest 'Parse response cookie with invalid flag (RFC 6265)' => sub {
220233
my $cookies = Mojo::Cookie::Response->parse(
221234
'foo="ba r"; Domain=.example.com; Path=/test; Max-Age=60;' . ' Expires=Thu, 07 Aug 2008 07:07:59 GMT; InSecure;');

0 commit comments

Comments
 (0)