php
58 lines · 8 steps
Signed remember-me cookies in PHP
A tamper-proof persistent-login cookie built from an HMAC-signed, base64-encoded payload.
Explained by
highlit
1<?php
2
3final class RememberMeCookie
4{
5 private const COOKIE_NAME = 'remember_me';
6 private const TTL = 60 * 60 * 24 * 30;
7
8 public function __construct(private readonly string $secret)
9 {
10 }
11
12 public function issue(int $userId, string $token): void
13 {
14 $expires = time() + self::TTL;
15 $payload = base64_encode(json_encode([
16 'uid' => $userId,
17 'tok' => $token,
18 'exp' => $expires,
19 ], JSON_THROW_ON_ERROR));
20
21 $value = $payload . '.' . $this->sign($payload);
22
23 setcookie(self::COOKIE_NAME, $value, [
24 'expires' => $expires,
25 'path' => '/',
26 'secure' => true,
27 'httponly' => true,
28 'samesite' => 'Lax',
29 ]);
30 }
31
32 public function verify(): ?array
33 {
34 $raw = $_COOKIE[self::COOKIE_NAME] ?? '';
35 if (!str_contains($raw, '.')) {
36 return null;
37 }
38
39 [$payload, $signature] = explode('.', $raw, 2);
40 $expected = $this->sign($payload);
41
42 if (!hash_equals($expected, $signature)) {
43 return null;
44 }
45
46 $data = json_decode((string) base64_decode($payload, true), true);
47 if (!is_array($data) || ($data['exp'] ?? 0) < time()) {
48 return null;
49 }
50
51 return ['user_id' => (int) $data['uid'], 'token' => (string) $data['tok']];
52 }
53
54 private function sign(string $payload): string
55 {
56 return hash_hmac('sha256', $payload, $this->secret);
57 }
58}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Signing a payload with HMAC lets you trust client-stored data without a server-side session.
- 2Always compare signatures with a constant-time function like hash_equals to avoid timing attacks.
- 3Embed and re-check an expiry inside the payload so old cookies can't be replayed forever.
Related explainers
php
<?php namespace App\Http\Middleware;
Caching HTTP responses with Laravel middleware
middleware
caching
http
Intermediate
8 steps
php
<?php namespace App\Validation;
Building a reusable address form validator in PHP
validation
error-accumulation
regex
Intermediate
9 steps
php
<?php namespace App\Http\Controllers;
A cached autocomplete endpoint in Laravel
caching
validation
query-ranking
Intermediate
8 steps
php
<?php declare(strict_types=1);
Normalizing human names in PHP
unicode
text-normalization
transliteration
Intermediate
8 steps
php
<?php namespace App\Http\Controllers;
Handling Stripe webhooks in Laravel
webhooks
signature-verification
dependency-injection
Intermediate
7 steps
php
<?php namespace App\FeatureFlags;
How a feature flag evaluator decides
feature-flags
rollout
hashing
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/signed-remember-me-cookies-in-php-explained-php-e841/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.