Blog · Security
Session Management with Timestamp Expiration
Math.floor(Date.now()/1000) > exp the session is expired. Always evaluate expiry using server-side clocks synchronized with NTP; never trust the browser for security decisions.Why Timestamps Are the Right Tool for Session Expiry
A session expiry is an instant on the timeline, not a wall-clock label in Chicago. Unix timestamps move that instant into a single integer comparable across regions, languages, and storage engines. The arithmetic is trivial: exp = issued + ttl. Compare to storing "expires Thursday 5pm" — you immediately inherit timezone, DST, and parsing bugs. Tokens and server-side session rows both benefit from the same representation; the only variation is whether your framework stores seconds (JWT NumericDate) or milliseconds (some cookie jars).
For distributed systems, timestamps let you reason about skew budgets. If two app nodes disagree by 200ms, a seconds-resolution expiry still behaves predictably; millisecond-sensitive policies need tighter infrastructure discipline. Document the maximum acceptable skew for your product and test failover scenarios where a node cold-starts with an unset clock — validation libraries should fail closed when wall time is unreasonable, rather than trusting the first packet seen.
JWT Expiration with Unix Timestamps
JSON Web Tokens carry iat, nbf, and exp as NumericDate values: seconds since 1970-01-01T00:00:00Z. Libraries perform the compare after signature verification — do not parse claims from unsigned tokens.
import jwt from 'jsonwebtoken';
const token = jwt.sign(
{ userId: 123, role: 'user' },
process.env.JWT_SECRET,
{ expiresIn: 3600 }
);
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded.exp);
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
/* redirect to login */
}
}import jwt
import time
payload = {
'user_id': 123,
'exp': int(time.time()) + 3600,
'iat': int(time.time()),
}
token = jwt.encode(payload, secret, algorithm='HS256')
try:
data = jwt.decode(token, secret, algorithms=['HS256'])
except jwt.ExpiredSignatureError:
passSliding Window Sessions
Sliding windows extend idle allowance on each authenticated request. Implement by bumping a server-side expires_at using the same formula each time: now + idle_budget. Redis provides atomic EXPIRE/EXPIREAT so your TTL tracks the computed wall instant.
async function touchSession(sessionId, ttl = 3600) {
const now = Math.floor(Date.now() / 1000);
const expiry = now + ttl;
await redis.hSet(`session:${sessionId}`, {
last_active: String(now),
expires_at: String(expiry),
});
await redis.expireAt(`session:${sessionId}`, expiry);
return expiry;
}
async function isSessionValid(sessionId) {
const session = await redis.hGetAll(`session:${sessionId}`);
if (!session.expires_at) return false;
return parseInt(session.expires_at, 10) > Math.floor(Date.now() / 1000);
}Common Timestamp Session Bugs
| Bug | Cause | Fix |
|---|---|---|
| Clock skew | Nodes disagree on "now" | NTP + compare only trusted gateway time |
| Timezone strings | Local SQL inserted into UTC columns | Normalize to UTC before persist |
| 32-bit wrap | Legacy INT exp in DB | BIGINT seconds or native timestamptz |
| Race on extend | Two requests update session row | Atomic Redis scripts or CAS row updates |
Token Refresh Pattern
Pair a short-lived access token (minutes) with a refresh token (days) stored hashed server-side. Each refresh rotation should mint a new refresh identifier and invalidate the old one; reuse of a retired refresh indicates theft. Encode only opaque IDs in cookies — not PII — and bind refresh tokens to device keys when possible.
// Pseudocode: rotation with explicit exp claims
function issueTokens(userId) {
const now = Math.floor(Date.now() / 1000);
const access = signJwt({ sub: userId, typ: 'access', exp: now + 900 }, SECRET);
const refresh = randomBytes(32).toString('hex');
storeRefreshHash(userId, hash(refresh), now + 60 * 60 * 24 * 30);
return { access, refresh };
}Key takeaways
- Use integers for expiry; seconds for JWT, ms only if every layer agrees.
- Verify signatures before interpreting time claims.
- Sliding sessions belong in data stores with atomic TTL updates.
- Refresh token reuse detection is mandatory for rotation schemes.
- Test leap-second and DST days even though epoch math is UTC-first.
Written by Unix Calculator Editorial Team — Senior Unix/Linux Engineers. Last verified May 2026.
Get the Unix Timestamp Cheatsheet
One email. Instant cheatsheet. No drip sequence.