Blog · Security

    Tutorial8 min read·Unix Calculator Editorial Team·Apr 18, 2026

    Session Management with Timestamp Expiration

    Quick answer: Store session expiry as a Unix timestamp (UTC seconds or milliseconds — pick one and document it). On each request, compare trusted server time to that value: if 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:
        pass

    Sliding 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

    BugCauseFix
    Clock skewNodes disagree on "now"NTP + compare only trusted gateway time
    Timezone stringsLocal SQL inserted into UTC columnsNormalize to UTC before persist
    32-bit wrapLegacy INT exp in DBBIGINT seconds or native timestamptz
    Race on extendTwo requests update session rowAtomic 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.

    Unix timestamp converter

    Get the Unix Timestamp Cheatsheet

    One email. Instant cheatsheet. No drip sequence.

    Advertisement