Implementing the "remember me" functionality using Cookies requires consideration of security, user experience, and persistent storage.
"Remember me" functionality principle
- Generate a long-lived authentication token after successful user login
- Store the token in a persistent Cookie
- Automatically use the token in the Cookie to complete login when the user visits again
Implementation solutions
Solution 1: Persistent Session Cookie
javascript// Server-side setting (Node.js Express) function setRememberMeCookie(res, token, rememberMe) { const options = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', path: '/' }; if (rememberMe) { // Long-term Cookie: 30 days options.maxAge = 30 * 24 * 60 * 60; } else { // Session Cookie: deleted when browser closes options.maxAge = null; } res.cookie('authToken', token, options); }
Solution 2: Dual token mechanism
javascript// Generate access token and refresh token function generateTokens(userId) { const accessToken = jwt.sign( { userId }, process.env.JWT_SECRET, { expiresIn: '15m' } // Short-term validity ); const refreshToken = crypto.randomBytes(32).toString('hex'); // Store refresh token in database db.saveRefreshToken(userId, refreshToken, { expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }); return { accessToken, refreshToken }; } // Set Cookies function setAuthCookies(res, tokens, rememberMe) { // Access token: short-term, HttpOnly res.cookie('accessToken', tokens.accessToken, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 15 * 60 // 15 minutes }); // Refresh token: long-term, HttpOnly if (rememberMe) { res.cookie('refreshToken', tokens.refreshToken, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 30 * 24 * 60 * 60 // 30 days }); } }
Security best practices
- Token generation
javascript// Use cryptographically secure random number generator const crypto = require('crypto'); function generateSecureToken() { return crypto.randomBytes(32).toString('hex'); }
- Token storage
javascript// Database storage solution const refreshTokenSchema = new Schema({ userId: { type: ObjectId, required: true }, token: { type: String, required: true, unique: true }, createdAt: { type: Date, default: Date.now }, expiresAt: { type: Date, required: true }, lastUsedAt: { type: Date, default: Date.now }, userAgent: String, ipAddress: String });
- Token verification
javascriptasync function verifyRefreshToken(token, req) { const record = await db.findRefreshToken(token); if (!record) { throw new Error('Invalid token'); } if (record.expiresAt < new Date()) { await db.deleteRefreshToken(token); throw new Error('Token expired'); } // Optional: verify User-Agent and IP if (record.userAgent !== req.headers['user-agent']) { await db.deleteRefreshToken(token); throw new Error('Token compromised'); } // Update last used time await db.updateRefreshToken(token, { lastUsedAt: new Date() }); return record.userId; }
User experience optimization
- Login form
html<form id="loginForm"> <input type="text" name="username" placeholder="Username" required> <input type="password" name="password" placeholder="Password" required> <label> <input type="checkbox" name="rememberMe"> Remember me (auto-login within 30 days) </label> <button type="submit">Login</button> </form>
- Auto-login flow
javascript// Check Cookie on page load async function checkAutoLogin() { const refreshToken = getCookie('refreshToken'); if (refreshToken) { try { const response = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'include' }); if (response.ok) { const { accessToken } = await response.json(); localStorage.setItem('accessToken', accessToken); // Redirect to home page window.location.href = '/dashboard'; } } catch (error) { console.error('Auto login failed:', error); } } }
Security enhancements
- Token rotation
javascript// Generate new refresh token each time refresh token is used async function rotateRefreshToken(oldToken) { const userId = await verifyRefreshToken(oldToken, req); // Delete old token await db.deleteRefreshToken(oldToken); // Generate new token const newToken = generateSecureToken(); await db.saveRefreshToken(userId, newToken, { expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }); return newToken; }
- Revocation mechanism
javascript// Revoke all tokens when user logs out async function logoutAllDevices(userId) { await db.deleteAllRefreshTokens(userId); res.clearCookie('accessToken'); res.clearCookie('refreshToken'); }
- Device management
javascript// Display list of logged-in devices async function getActiveDevices(userId) { const tokens = await db.findRefreshTokensByUser(userId); return tokens.map(token => ({ device: parseUserAgent(token.userAgent), lastUsed: token.lastUsedAt, current: token.userAgent === req.headers['user-agent'] })); }