javascript
53 lines · 9 steps
Remember-me login with signed cookies in Express
An Express router that logs users in and issues a tamper-proof persistent cookie for staying signed in.
Explained by
highlit
1const express = require('express');
2const cookieParser = require('cookie-parser');
3
4const router = express.Router();
5
6router.use(cookieParser(process.env.COOKIE_SECRET));
7
8const REMEMBER_COOKIE = 'remember_me';
9const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
10
11router.post('/login', async (req, res) => {
12 const { email, password, rememberMe } = req.body;
13
14 const user = await User.authenticate(email, password);
15 if (!user) {
16 return res.status(401).json({ error: 'Invalid credentials' });
17 }
18
19 req.session.userId = user.id;
20
21 if (rememberMe) {
22 const token = await user.issueRememberToken();
23 res.cookie(REMEMBER_COOKIE, token, {
24 signed: true,
25 httpOnly: true,
26 secure: process.env.NODE_ENV === 'production',
27 sameSite: 'lax',
28 maxAge: THIRTY_DAYS,
29 });
30 } else {
31 res.clearCookie(REMEMBER_COOKIE);
32 }
33
34 res.json({ id: user.id, email: user.email });
35});
36
37router.get('/session', async (req, res) => {
38 const token = req.signedCookies[REMEMBER_COOKIE];
39 if (!token) {
40 return res.status(204).end();
41 }
42
43 const user = await User.findByRememberToken(token);
44 if (!user) {
45 res.clearCookie(REMEMBER_COOKIE);
46 return res.status(204).end();
47 }
48
49 req.session.userId = user.id;
50 res.json({ id: user.id, email: user.email });
51});
52
53module.exports = router;
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Signing cookies with a secret lets the server detect tampering without storing the value server-side.
- 2Flags like httpOnly, secure, and sameSite harden auth cookies against XSS and CSRF vectors.
- 3A remember-me token should be re-validated against the database on every session restore, not trusted blindly.
Related explainers
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
javascript
import { useEffect, useRef, useState } from 'react'; export function useDelayedFlag(active, delay = 300) { const [visible, setVisible] = useState(false);
Delaying a loading spinner with a React hook
custom-hooks
debouncing
cleanup
Intermediate
8 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
javascript
const SWIPE_THRESHOLD = 80; const MAX_TRANSLATE = 120; export function attachSwipeToDismiss(element, onDismiss) {
Building a swipe-to-dismiss gesture in JS
touch-events
gesture-detection
dom-manipulation
Intermediate
10 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/remember-me-login-with-signed-cookies-in-express-explained-javascript-7e9e/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.