Skip to content

Commit de5988d

Browse files
committed
added SameSite enum; setCookie() and Session accept it, IResponse::SameSite* constants deprecated
1 parent 00a4f7b commit de5988d

7 files changed

Lines changed: 40 additions & 16 deletions

File tree

src/Bridges/HttpDI/SessionExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace Nette\Bridges\HttpDI;
99

1010
use Nette;
11-
use Nette\Http\IResponse;
11+
use Nette\Http\SameSite;
1212
use Nette\Schema\Expect;
1313

1414

@@ -42,7 +42,7 @@ public function getConfigSchema(): Nette\Schema\Schema
4242
'expiration' => Expect::string()->dynamic(),
4343
'handler' => Expect::string()->dynamic(),
4444
'readAndClose' => Expect::bool(),
45-
'cookieSamesite' => Expect::anyOf(IResponse::SameSiteLax, IResponse::SameSiteStrict, IResponse::SameSiteNone)
45+
'cookieSamesite' => Expect::anyOf(SameSite::Lax->value, SameSite::Strict->value, SameSite::None->value)
4646
->firstIsDefault(),
4747
])->otherItems();
4848
}

src/Http/Helpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static function ipMatch(string $ip, string $mask): bool
120120
public static function initCookie(IRequest $request, IResponse $response): void
121121
{
122122
if ($request->getHeader('Sec-Fetch-Site') === null) {
123-
$response->setCookie(self::StrictCookieName, '1', null, '/', sameSite: IResponse::SameSiteStrict);
123+
$response->setCookie(self::StrictCookieName, '1', null, '/', sameSite: SameSite::Strict);
124124
}
125125
}
126126
}

src/Http/IResponse.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ interface IResponse
138138
511 => 'Network Authentication Required',
139139
];
140140

141-
/** SameSite cookie */
141+
/** @deprecated use Nette\Http\SameSite enum */
142142
public const
143143
SameSiteLax = 'Lax',
144144
SameSiteStrict = 'Strict',
@@ -147,13 +147,13 @@ interface IResponse
147147
/** @deprecated use IResponse::ReasonPhrases */
148148
public const REASON_PHRASES = self::ReasonPhrases;
149149

150-
/** @deprecated use IResponse::SameSiteLax */
150+
/** @deprecated use Nette\Http\SameSite enum */
151151
public const SAME_SITE_LAX = self::SameSiteLax;
152152

153-
/** @deprecated use IResponse::SameSiteStrict */
153+
/** @deprecated use Nette\Http\SameSite enum */
154154
public const SAME_SITE_STRICT = self::SameSiteStrict;
155155

156-
/** @deprecated use IResponse::SameSiteNone */
156+
/** @deprecated use Nette\Http\SameSite enum */
157157
public const SAME_SITE_NONE = self::SameSiteNone;
158158

159159
/** @deprecated use IResponse::S100_Continue */

src/Http/Response.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public function getHeaders(): array
225225

226226
/**
227227
* Sends a cookie.
228-
* @param self::SameSite*|null $sameSite
228+
* @param SameSite|self::SameSite*|null $sameSite
229229
* @throws Nette\InvalidStateException if HTTP headers have been sent
230230
*/
231231
public function setCookie(
@@ -236,11 +236,12 @@ public function setCookie(
236236
?string $domain = null,
237237
?bool $secure = null,
238238
?bool $httpOnly = null,
239-
?string $sameSite = null,
239+
SameSite|string|null $sameSite = null,
240240
bool $partitioned = false,
241241
): static
242242
{
243243
self::checkHeaders();
244+
$sameSite = $sameSite instanceof SameSite ? $sameSite->value : $sameSite;
244245
[$path, $domain] = [
245246
$path ?? ($domain ? '/' : $this->cookiePath),
246247
$domain ?? ($path ? '' : $this->cookieDomain),
@@ -254,9 +255,9 @@ public function setCookie(
254255
}
255256

256257
$seconds = Helpers::expirationToSeconds($expire);
257-
$sameSite ??= self::SameSiteLax;
258+
$sameSite ??= SameSite::Lax->value;
258259
// both SameSite=None and Partitioned are rejected by the browser without the Secure attribute
259-
$secure = $sameSite === self::SameSiteNone || $partitioned || ($secure ?? $this->cookieSecure);
260+
$secure = $sameSite === SameSite::None->value || $partitioned || ($secure ?? $this->cookieSecure);
260261
$cookie = $name . '=' . rawurlencode($value)
261262
. ($seconds === null ? '' : '; expires=' . Helpers::formatDate(time() + $seconds) . '; Max-Age=' . max(0, $seconds))
262263
. '; path=' . $path

src/Http/Session.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Session
4040

4141
/** @var array<string, mixed> default configuration */
4242
private array $options = [
43-
'cookie_samesite' => IResponse::SameSiteLax,
43+
'cookie_samesite' => SameSite::Lax->value,
4444
'cookie_lifetime' => 0, // session cookie - kept by the browser per its own policy
4545
'gc_maxlifetime' => self::DefaultFileLifetime, // server-side idle timeout, independent of the cookie lifetime above
4646
];
@@ -509,14 +509,14 @@ public function setCookieParameters(
509509
string $path,
510510
?string $domain = null,
511511
?bool $secure = null,
512-
?string $sameSite = null,
512+
SameSite|string|null $sameSite = null,
513513
): static
514514
{
515515
return $this->setOptions([
516516
'cookie_path' => $path,
517517
'cookie_domain' => $domain,
518518
'cookie_secure' => $secure,
519-
'cookie_samesite' => $sameSite,
519+
'cookie_samesite' => $sameSite instanceof SameSite ? $sameSite->value : $sameSite,
520520
]);
521521
}
522522

src/Http/enums.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,14 @@ enum FetchDest: string
6060
case Worker = 'worker';
6161
case Xslt = 'xslt';
6262
}
63+
64+
65+
/**
66+
* Values of the SameSite cookie attribute.
67+
*/
68+
enum SameSite: string
69+
{
70+
case Lax = 'Lax';
71+
case Strict = 'Strict';
72+
case None = 'None';
73+
}

tests/Http/Response.setCookie.phpt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,19 +138,31 @@ Assert::exception(
138138
// SameSite=None forces the Secure attribute (otherwise the browser rejects the cookie)
139139
$response = new Http\Response;
140140
$old = headers_list();
141-
$response->setCookie('test', 'value', null, sameSite: Http\IResponse::SameSiteNone);
141+
$response->setCookie('test', 'value', null, sameSite: Http\SameSite::None);
142142
$headers = array_values(array_diff(headers_list(), $old, ['Set-Cookie:']));
143143
Assert::same(['Set-Cookie: test=value; path=/; secure; HttpOnly; SameSite=None'], $headers);
144144

145145

146146
// Partitioned (CHIPS) adds the attribute and forces Secure
147147
$response = new Http\Response;
148148
$old = headers_list();
149-
$response->setCookie('test', 'value', null, sameSite: Http\IResponse::SameSiteNone, partitioned: true);
149+
$response->setCookie('test', 'value', null, sameSite: Http\SameSite::None, partitioned: true);
150150
$headers = array_values(array_diff(headers_list(), $old, ['Set-Cookie:']));
151151
Assert::same(['Set-Cookie: test=value; path=/; secure; HttpOnly; SameSite=None; Partitioned'], $headers);
152152

153153

154+
// the SameSite enum and the equivalent string produce the same header
155+
$response = new Http\Response;
156+
$old = headers_list();
157+
$response->setCookie('test', 'a', null, sameSite: Http\SameSite::Strict);
158+
$response->setCookie('test', 'b', null, sameSite: 'Strict');
159+
$headers = array_values(array_diff(headers_list(), $old, ['Set-Cookie:']));
160+
Assert::same([
161+
'Set-Cookie: test=a; path=/; HttpOnly; SameSite=Strict',
162+
'Set-Cookie: test=b; path=/; HttpOnly; SameSite=Strict',
163+
], $headers);
164+
165+
154166
// integer 0 is deprecated, but kept as a session cookie for BC
155167
$response = new Http\Response;
156168
$old = headers_list();

0 commit comments

Comments
 (0)