trim extraneous space
[catagits/Catalyst-Plugin-Session-State-Cookie.git] / lib / Catalyst / Plugin / Session / State / Cookie.pm
CommitLineData
1a776a0c 1package Catalyst::Plugin::Session::State::Cookie;
679f4a58 2use Moose;
3use namespace::autoclean;
bf2bce67 4
679f4a58 5extends 'Catalyst::Plugin::Session::State';
bf2bce67 6
45c96680 7use MRO::Compat;
74586782 8use Catalyst::Utils ();
bf2bce67 9
7ef7785c 10our $VERSION = '0.18';
28b6b575 11$VERSION =~ tr/_//d;
ea139a65 12
679f4a58 13has _deleted_session_id => ( is => 'rw' );
81eb8ebf 14
5e50008f 15sub setup_session {
20e33791 16 my $c = shift;
5e50008f 17
45c96680 18 $c->maybe::next::method(@_);
2bde9162 19
5aca1bdd 20 $c->_session_plugin_config->{cookie_name}
7022ec4c 21 ||= Catalyst::Utils::appprefix($c) . '_session';
5e50008f 22}
23
0ff18b66 24sub extend_session_id {
25 my ( $c, $sid, $expires ) = @_;
1a776a0c 26
2bde9162 27 if ( my $cookie = $c->get_session_cookie ) {
0ff18b66 28 $c->update_session_cookie( $c->make_session_cookie( $sid ) );
58730edc 29 }
db1cda22 30
45c96680 31 $c->maybe::next::method( $sid, $expires );
2bde9162 32}
33
34sub set_session_id {
35 my ( $c, $sid ) = @_;
36
37 $c->update_session_cookie( $c->make_session_cookie( $sid ) );
38
45c96680 39 return $c->maybe::next::method($sid);
db1cda22 40}
41
42sub update_session_cookie {
58730edc 43 my ( $c, $updated ) = @_;
ab649d6b 44
8bdcbb46 45 unless ( $c->cookie_is_rejecting( $updated ) ) {
5aca1bdd 46 my $cookie_name = $c->_session_plugin_config->{cookie_name};
8bdcbb46 47 $c->response->cookies->{$cookie_name} = $updated;
48 }
49}
50
51sub cookie_is_rejecting {
52 my ( $c, $cookie ) = @_;
ab649d6b 53
8bdcbb46 54 if ( $cookie->{path} ) {
91e4fe2d 55 return 1 if index '/'.$c->request->path, $cookie->{path};
8bdcbb46 56 }
ab649d6b 57
8bdcbb46 58 return 0;
db1cda22 59}
5e50008f 60
db1cda22 61sub make_session_cookie {
2bde9162 62 my ( $c, $sid, %attrs ) = @_;
58730edc 63
5aca1bdd 64 my $cfg = $c->_session_plugin_config;
58730edc 65 my $cookie = {
2bde9162 66 value => $sid,
58730edc 67 ( $cfg->{cookie_domain} ? ( domain => $cfg->{cookie_domain} ) : () ),
8bdcbb46 68 ( $cfg->{cookie_path} ? ( path => $cfg->{cookie_path} ) : () ),
df55e818 69 %attrs,
58730edc 70 };
71
2bde9162 72 unless ( exists $cookie->{expires} ) {
73 $cookie->{expires} = $c->calculate_session_cookie_expires();
74 }
1e986fd5 75
d2cf2047 76 #beware: we have to accept also the old syntax "cookie_secure = true"
77 my $sec = $cfg->{cookie_secure} || 0; # default = 0 (not set)
78 $cookie->{secure} = 1 unless ( ($sec==0) || ($sec==2) );
ab649d6b 79 $cookie->{secure} = 1 if ( ($sec==2) && $c->req->secure );
80
5aca1bdd 81 $cookie->{httponly} = $cfg->{cookie_httponly};
3c6b7451 82 $cookie->{httponly} = 1
5aca1bdd 83 unless defined $cookie->{httponly}; # default = 1 (set httponly)
fc4b9d6d 84
f877359e 85 $cookie->{samesite} = $cfg->{cookie_samesite};
86 $cookie->{samesite} = "Lax"
e09e1a5c 87 unless defined $cookie->{samesite}; # default = Lax
f877359e 88
1e986fd5 89 return $cookie;
90}
91
2bde9162 92sub calc_expiry { # compat
93 my $c = shift;
45c96680 94 $c->maybe::next::method( @_ ) || $c->calculate_session_cookie_expires( @_ );
2bde9162 95}
96
97sub calculate_session_cookie_expires {
98 my $c = shift;
5aca1bdd 99 my $cfg = $c->_session_plugin_config;
2bde9162 100
45c96680 101 my $value = $c->maybe::next::method(@_);
1e986fd5 102 return $value if $value;
2bde9162 103
58730edc 104 if ( exists $cfg->{cookie_expires} ) {
7022ec4c 105 if ( $cfg->{cookie_expires} > 0 ) {
1e986fd5 106 return time() + $cfg->{cookie_expires};
7022ec4c 107 }
108 else {
1e986fd5 109 return undef;
7022ec4c 110 }
58730edc 111 }
112 else {
2bde9162 113 return $c->session_expires;
58730edc 114 }
bf2bce67 115}
116
2bde9162 117sub get_session_cookie {
bf2bce67 118 my $c = shift;
1a776a0c 119
5aca1bdd 120 my $cookie_name = $c->_session_plugin_config->{cookie_name};
5e50008f 121
2bde9162 122 return $c->request->cookies->{$cookie_name};
123}
124
125sub get_session_id {
126 my $c = shift;
127
ab649d6b 128 if ( !$c->_deleted_session_id and my $cookie = $c->get_session_cookie ) {
bf2bce67 129 my $sid = $cookie->value;
bf2bce67 130 $c->log->debug(qq/Found sessionid "$sid" in cookie/) if $c->debug;
2bde9162 131 return $sid if $sid;
bf2bce67 132 }
bf2bce67 133
45c96680 134 $c->maybe::next::method(@_);
2bde9162 135}
136
137sub delete_session_id {
df55e818 138 my ( $c, $sid ) = @_;
ab649d6b 139
ea139a65 140 $c->_deleted_session_id(1); # to prevent get_session_id from returning it
df55e818 141
142 $c->update_session_cookie( $c->make_session_cookie( $sid, expires => 0 ) );
143
45c96680 144 $c->maybe::next::method($sid);
bf2bce67 145}
146
58b69507 1471;
1a776a0c 148__END__
bf2bce67 149
1a776a0c 150=head1 NAME
bf2bce67 151
75d3560d 152Catalyst::Plugin::Session::State::Cookie - Maintain session IDs using cookies.
bf2bce67 153
1a776a0c 154=head1 SYNOPSIS
bf2bce67 155
20e33791 156 use Catalyst qw/Session Session::State::Cookie Session::Store::Foo/;
bf2bce67 157
1a776a0c 158=head1 DESCRIPTION
bf2bce67 159
1a776a0c 160In order for L<Catalyst::Plugin::Session> to work the session ID needs to be
161stored on the client, and the session data needs to be stored on the server.
bf2bce67 162
1a776a0c 163This plugin stores the session ID on the client using the cookie mechanism.
57dbf608 164
724a6173 165=head1 METHODS
166
167=over 4
168
169=item make_session_cookie
170
171Returns a hash reference with the default values for new cookies.
172
173=item update_session_cookie $hash_ref
174
175Sets the cookie based on C<cookie_name> in the response object.
176
2cfb85de 177=item calc_expiry
178
179=item calculate_session_cookie_expires
180
181=item cookie_is_rejecting
182
183=item delete_session_id
184
185=item extend_session_id
186
187=item get_session_cookie
188
189=item get_session_id
190
191=item set_session_id
192
724a6173 193=back
194
1a776a0c 195=head1 EXTENDED METHODS
58c05d1a 196
57dbf608 197=over 4
198
1a776a0c 199=item prepare_cookies
57dbf608 200
1a776a0c 201Will restore if an appropriate cookie is found.
58c05d1a 202
d52e5079 203=item finalize_cookies
58c05d1a 204
1536d9aa 205Will set a cookie called C<session> if it doesn't exist or if its value is not
19c2baa1 206the current session id.
207
208=item setup_session
209
1536d9aa 210Will set the C<cookie_name> parameter to its default value if it isn't set.
58c05d1a 211
57dbf608 212=back
58c05d1a 213
5e50008f 214=head1 CONFIGURATION
215
216=over 4
217
218=item cookie_name
219
ae33e13f 220The name of the cookie to store (defaults to C<Catalyst::Utils::apprefix($c) . '_session'>).
5e50008f 221
41b4b15c 222=item cookie_domain
223
224The name of the domain to store in the cookie (defaults to current host)
225
7022ec4c 226=item cookie_expires
227
ab649d6b 228Number of seconds from now you want to elapse before cookie will expire.
229Set to 0 to create a session cookie, ie one which will die when the
7022ec4c 230user's browser is shut down.
231
fc4b9d6d 232=item cookie_secure
233
d2cf2047 234If this attribute B<set to 0> the cookie will not have the secure flag.
235
ab649d6b 236If this attribute B<set to 1> (or true for backward compatibility) - the cookie
e94e9e1c 237sent by the server to the client will get the secure flag that tells the browser
238to send this cookie back to the server only via HTTPS.
d2cf2047 239
e94e9e1c 240If this attribute B<set to 2> then the cookie will get the secure flag only if
ab649d6b 241the request that caused cookie generation was sent over https (this option is
e94e9e1c 242not good if you are mixing https and http in your application).
d2cf2047 243
e94e9e1c 244Default value is 0.
d2cf2047 245
246=item cookie_httponly
247
248If this attribute B<set to 0>, the cookie will not have HTTPOnly flag.
249
ab649d6b 250If this attribute B<set to 1>, the cookie will got HTTPOnly flag that should
d2cf2047 251prevent client side Javascript accessing the cookie value - this makes some
252sort of session hijacking attacks significantly harder. Unfortunately not all
ab649d6b 253browsers support this flag (MSIE 6 SP1+, Firefox 3.0.0.6+, Opera 9.5+); if
d2cf2047 254a browser is not aware of HTTPOnly the flag will be ignored.
255
256Default value is 1.
257
2b79283a 258Note1: Many people are confused by the name "HTTPOnly" - it B<does not mean>
ab649d6b 259that this cookie works only over HTTP and not over HTTPS.
d2cf2047 260
ef0cdc56 261Note2: This parameter requires Catalyst::Runtime 5.80005 otherwise is skipped.
fc4b9d6d 262
f877359e 263=item cookie_samesite
264
265This attribute configures the value of the
266L<SameSite|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite>
267flag.
268
269If set to None, the cookie will be sent when making cross origin requests,
270including following links from other origins. This requires the
271L</cookie_secure> flag to be set.
272
273If set to Lax, the cookie will not be included when embedded in or fetched from
274other origins, but will be included when following cross origin links.
275
276If set to Strict, the cookie will not be included for any cross origin requests,
277including links from different origins.
278
279Default value is C<Lax>. This is the default modern browsers use.
280
281Note: This parameter requires Catalyst::Runtime 5.90125 otherwise is skipped.
282
8bdcbb46 283=item cookie_path
284
285The path of the request url where cookie should be baked.
286
5e50008f 287=back
288
d6bdceb5 289For example, you could stick this in MyApp.pm:
290
58b69507 291 __PACKAGE__->config( 'Plugin::Session' => {
292 cookie_domain => '.mydomain.com',
293 });
d6bdceb5 294
724a6173 295=head1 CAVEATS
db1cda22 296
297Sessions have to be created before the first write to be saved. For example:
298
58b69507 299 sub action : Local {
300 my ( $self, $c ) = @_;
301 $c->res->write("foo");
302 $c->session( ... );
303 ...
304 }
db1cda22 305
306Will cause a session ID to not be set, because by the time a session is
307actually created the headers have already been sent to the client.
308
bf2bce67 309=head1 SEE ALSO
310
1a776a0c 311L<Catalyst>, L<Catalyst::Plugin::Session>.
bf2bce67 312
47f47da5 313=head1 AUTHORS
bf2bce67 314
58b69507 315Yuval Kogman <nothingmuch@woobling.org>
8ae6d944 316
317=head1 CONTRIBUTORS
318
47f47da5 319This module is derived from L<Catalyst::Plugin::Session::FastMmap> code, and
320has been heavily modified since.
321
58b69507 322Andrew Ford
323
324Andy Grundman
325
326Christian Hansen
327
328Marcus Ramberg
329
330Jonathan Rockway <jrockway@cpan.org>
331
332Sebastian Riedel
333
334Florian Ragwitz
bf2bce67 335
336=head1 COPYRIGHT
337
d612a8fc 338Copyright (c) 2005 - 2009
339the Catalyst::Plugin::Session::State::Cookie L</AUTHORS> and L</CONTRIBUTORS>
340as listed above.
bab1512d 341
342=head1 LICENSE
343
bfeb5ca0 344This program is free software, you can redistribute it and/or modify it
345under the same terms as Perl itself.
bf2bce67 346
347=cut