[Bug]: Token Expiration Bypass in @segment/analytics-node 2.3.0

Open
#1,338 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
45/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Stale
Tech stack
typescript

Research direction

Start in src/lib/token-manager.ts at isValidToken around lines 320-326, then read the OAuth token handling around line 137 where expires_at is calculated. Reproduce the OAuth flow with an expired token and verify that expiration validation rejects it and triggers refresh rather than reusing the cached token.

Written by the indexing model from the issue text.

Description

Summary

The isValidToken method in the TokenManager class contains a logic error that causes token expiration checks to always pass, effectively disabling token expiration validation entirely.


Vulnerable Code

// src/lib/token-manager.ts, lines 320-326
isValidToken(token?: AccessToken): token is AccessToken {
  return (
    typeof token !== 'undefined' &&
    token !== null &&
    token.expires_in < Date.now() / 1000  // BUG: Wrong field and wrong comparison
  )
}

Root Cause

The validation logic has two compounding errors:

  1. Wrong field: The code checks expires_in (a duration in seconds, e.g., 3600) instead of expires_at (the actual expiration timestamp).

  2. Wrong comparison operator: Even if the correct field were used, the comparison should check if the expiration time is greater than the current time, not less than.

How tokens are structured

When a token is received (line 137), the code correctly calculates expires_at:

token.expires_at = Math.round(Date.now() / 1000) + token.expires_in

For a token with expires_in: 3600 (1 hour):

  • expires_at = current timestamp + 3600 ≈ 1707004600
  • expires_in = 3600
Why the bug occurs

The current check token.expires_in < Date.now() / 1000 evaluates:

3600 < 1707000000  →  true (always)

This condition is always true for any realistic token, meaning:

  • Expired tokens are incorrectly considered valid
  • Token refresh is never triggered based on expiration
  • The SDK continues using stale/expired tokens

Impact

  • Expired tokens remain in use: The SDK will continue sending requests with expired OAuth tokens until an external failure occurs.
  • Authentication failures: Requests made with expired tokens will fail at the Segment API, causing data loss or delivery failures.
  • Silent failures: Depending on error handling, these failures may go unnoticed.

Suggested Fix

isValidToken(token?: AccessToken): token is AccessToken {
  return (
    typeof token !== 'undefined' &&
    token !== null &&
    typeof token.expires_at === 'number' &&
    token.expires_at > Date.now() / 1000  // Use expires_at with correct comparison
  )
}

Reproduction

Any usage of the OAuth flow in @segment/analytics-node is affected. The bug can be confirmed by:

  1. Obtaining a valid OAuth token
  2. Waiting for the token to expire
  3. Observing that isValidToken() still returns true
  4. Observing that the cached expired token continues to be used
Dominant language
TypeScript
Stars
477
Forks
160
Avg merge
6h 19m
Merged PRs (30d)
4

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from segmentio/analytics-next

All issues in segmentio/analytics-next

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.