Skip to main content

Overview

The isSessionTokenValid() function validates session tokens to ensure they haven’t expired and match the expected format. It checks token structure, timestamp validity, optional user ID matching, and expiration times.

Common Use Cases

  • Session management and authentication
  • Token-based authentication validation
  • API request authentication
  • Stateless session validation
  • Token expiration checking

Function Signature

isSessionTokenValid(token, options)

Parameters

token
string
required
The session token to validate.
options
object
Configuration options for token validation.
options.length
number
default:"32"
The length of the random part of the token. Must match the length used when generating the token.
options.userID
string | null
default:"null"
The user ID associated with the session. If provided, the validator will verify it matches the token.
options.expiresIn
string
default:"'1h'"
The expiration time for the token. Format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days). Examples: '30m', '1h', '7d', '3600s'
options.includeTimestamp
boolean
default:"true"
Whether the token includes a timestamp. Must be true to validate expiration.
options.details
boolean
default:"false"
Whether to return detailed validation information instead of a boolean.

Return Value

result
boolean | object
When details is false: Returns true if token is valid and not expired, false otherwise.When details is true: Returns an object with the following properties:
valid
boolean
Whether the token is valid and not expired.
errors
string[] | null
Array of validation error messages, or null if valid.
token
string
The token that was validated.
timestamp
number
The timestamp extracted from the token (milliseconds since epoch).
expiresAt
number
The expiration timestamp (milliseconds since epoch).
remainingTime
number
Time remaining until expiration in milliseconds. Negative if expired.

Examples

Basic Token Validation

import { isSessionTokenValid } from 'validauth';

// Assume you have a token generated with default settings
const token = 'abc123...timestamp...'; // Your actual token

// Validate with default settings (1 hour expiration)
const isValid = isSessionTokenValid(token, {
  expiresIn: '1h'
});

if (isValid) {
  console.log('Token is valid');
} else {
  console.log('Token is invalid or expired');
}

Session Management Example

import { isSessionTokenValid } from 'validauth';
import { generateSessionToken } from 'validauth'; // Assuming this exists

class SessionManager {
  constructor() {
    this.sessions = new Map();
  }

  // Create a new session
  createSession(userID) {
    // Generate token (you'd implement this based on your token generation)
    const token = this.generateToken(userID);
    
    this.sessions.set(token, {
      userID: userID,
      createdAt: Date.now(),
      lastActivity: Date.now()
    });

    return token;
  }

  // Validate existing session
  validateSession(token, userID) {
    const result = isSessionTokenValid(token, {
      length: 32,
      userID: userID,
      expiresIn: '1h',
      includeTimestamp: true,
      details: true
    });

    if (!result.valid) {
      // Clean up expired session
      this.sessions.delete(token);
      return {
        valid: false,
        message: this.getErrorMessage(result.errors)
      };
    }

    // Update last activity
    const session = this.sessions.get(token);
    if (session) {
      session.lastActivity = Date.now();
    }

    return {
      valid: true,
      remainingTime: result.remainingTime,
      expiresAt: result.expiresAt
    };
  }

  // Check if token needs refresh (e.g., less than 10 minutes remaining)
  shouldRefreshToken(token, userID) {
    const result = isSessionTokenValid(token, {
      userID: userID,
      expiresIn: '1h',
      details: true
    });

    if (!result.valid) {
      return false;
    }

    // Refresh if less than 10 minutes remaining
    const tenMinutes = 10 * 60 * 1000;
    return result.remainingTime < tenMinutes;
  }

  getErrorMessage(errors) {
    if (!errors || errors.length === 0) {
      return 'Unknown error';
    }
    
    const errorMap = {
      'Token has expired': 'Your session has expired. Please log in again.',
      'UserID mismatch in token': 'Invalid session. Please log in again.',
      'Invalid timestamp in token': 'Invalid session token.',
      'Token must be a non-empty string': 'No session token provided.'
    };

    return errorMap[errors[0]] || errors[0];
  }

  generateToken(userID) {
    // Simplified token generation
    const randomPart = Math.random().toString(36).substring(2, 34);
    const timestamp = Date.now().toString();
    return randomPart + timestamp + userID;
  }
}

// Usage
const sessionManager = new SessionManager();

// Create session
const token = sessionManager.createSession('user123');

// Validate session
const validation = sessionManager.validateSession(token, 'user123');
if (validation.valid) {
  console.log('Session is active');
  
  // Check if token should be refreshed
  if (sessionManager.shouldRefreshToken(token, 'user123')) {
    console.log('Token should be refreshed');
  }
} else {
  console.log(validation.message);
}

API Authentication Middleware

import { isSessionTokenValid } from 'validauth';

function authMiddleware(options = {}) {
  const defaultOptions = {
    tokenHeader: 'Authorization',
    tokenPrefix: 'Bearer ',
    expiresIn: '1h',
    ...options
  };

  return async (req, res, next) => {
    try {
      // Extract token from header
      const authHeader = req.headers[defaultOptions.tokenHeader.toLowerCase()];
      
      if (!authHeader) {
        return res.status(401).json({
          error: 'No authorization token provided'
        });
      }

      // Remove Bearer prefix if present
      const token = authHeader.startsWith(defaultOptions.tokenPrefix)
        ? authHeader.slice(defaultOptions.tokenPrefix.length)
        : authHeader;

      // Validate token
      const result = isSessionTokenValid(token, {
        length: 32,
        expiresIn: defaultOptions.expiresIn,
        includeTimestamp: true,
        details: true
      });

      if (!result.valid) {
        return res.status(401).json({
          error: 'Invalid or expired token',
          details: result.errors
        });
      }

      // Attach token info to request
      req.token = token;
      req.tokenInfo = {
        timestamp: result.timestamp,
        expiresAt: result.expiresAt,
        remainingTime: result.remainingTime
      };

      // Warn if token is expiring soon (less than 5 minutes)
      if (result.remainingTime < 5 * 60 * 1000) {
        res.setHeader('X-Token-Expiring', 'true');
        res.setHeader('X-Token-Remaining', result.remainingTime.toString());
      }

      next();
    } catch (error) {
      console.error('Auth middleware error:', error);
      res.status(500).json({
        error: 'Authentication error'
      });
    }
  };
}

// Usage with Express
// app.use('/api', authMiddleware({ expiresIn: '2h' }));

Token Refresh Strategy

import { isSessionTokenValid } from 'validauth';

class TokenRefreshManager {
  constructor(refreshThresholdMs = 10 * 60 * 1000) { // 10 minutes
    this.refreshThreshold = refreshThresholdMs;
  }

  // Check if token needs refresh
  needsRefresh(token, expiresIn = '1h') {
    const result = isSessionTokenValid(token, {
      expiresIn: expiresIn,
      details: true
    });

    if (!result.valid) {
      return {
        needsRefresh: true,
        reason: 'expired',
        errors: result.errors
      };
    }

    if (result.remainingTime < this.refreshThreshold) {
      return {
        needsRefresh: true,
        reason: 'expiring-soon',
        remainingTime: result.remainingTime
      };
    }

    return {
      needsRefresh: false,
      remainingTime: result.remainingTime
    };
  }

  // Refresh token if needed
  async autoRefresh(token, refreshCallback) {
    const check = this.needsRefresh(token);

    if (check.needsRefresh) {
      console.log(`Refreshing token: ${check.reason}`);
      return await refreshCallback();
    }

    return token;
  }
}

// Usage
const refreshManager = new TokenRefreshManager();

// Check if refresh is needed
const check = refreshManager.needsRefresh(currentToken, '1h');
if (check.needsRefresh) {
  console.log(`Token needs refresh: ${check.reason}`);
  // Request new token from server
}

// Auto-refresh example
const token = await refreshManager.autoRefresh(
  currentToken,
  async () => {
    // Your token refresh logic
    const response = await fetch('/api/refresh-token', {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${currentToken}` }
    });
    const data = await response.json();
    return data.newToken;
  }
);

Multi-Tier Session Expiration

import { isSessionTokenValid } from 'validauth';

class TieredSessionManager {
  validateWithTiers(token, userID, sessionType = 'standard') {
    // Different expiration times for different session types
    const sessionTiers = {
      'short': '30m',    // Quick actions
      'standard': '1h',  // Normal browsing
      'extended': '24h', // Remember me
      'api': '7d'        // API keys
    };

    const expiresIn = sessionTiers[sessionType] || sessionTiers.standard;

    const result = isSessionTokenValid(token, {
      length: 32,
      userID: userID,
      expiresIn: expiresIn,
      includeTimestamp: true,
      details: true
    });

    if (!result.valid) {
      return {
        valid: false,
        message: 'Session expired or invalid',
        errors: result.errors
      };
    }

    // Calculate time remaining in a friendly format
    const timeRemaining = this.formatTimeRemaining(result.remainingTime);

    return {
      valid: true,
      sessionType: sessionType,
      expiresIn: expiresIn,
      timeRemaining: timeRemaining,
      shouldWarn: result.remainingTime < 5 * 60 * 1000 // Warn if < 5 min
    };
  }

  formatTimeRemaining(ms) {
    const hours = Math.floor(ms / (60 * 60 * 1000));
    const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));
    
    if (hours > 0) {
      return `${hours}h ${minutes}m`;
    }
    return `${minutes}m`;
  }
}

// Usage
const tieredManager = new TieredSessionManager();

const result = tieredManager.validateWithTiers(token, 'user123', 'extended');
if (result.valid) {
  console.log(`Session valid for ${result.timeRemaining}`);
  
  if (result.shouldWarn) {
    console.warn('Session expiring soon!');
  }
}

Validation Rules

Token Structure Requirements
  • Token must be a non-empty string
  • Must include timestamp when includeTimestamp is true
  • Token format: [random_part][timestamp][userID] (if userID is included)
  • Timestamp must be parseable as an integer
  • Current time must be within the expiration window

Expiration Time Formats

Valid expiresIn formats:
  • '30s' - 30 seconds
  • '10m' - 10 minutes
  • '1h' - 1 hour
  • '7d' - 7 days

Error Messages

The validator returns these error messages:
  • "Token must be a non-empty string" - Token is missing or not a string
  • "Token must include timestamp to validate expiration" - includeTimestamp is false
  • "Invalid expiresIn format (use format like '1h', '30m')" - Malformed expiration time
  • "Invalid time unit in expiresIn" - Unknown time unit
  • "UserID mismatch in token" - Token’s userID doesn’t match provided userID
  • "Invalid timestamp in token" - Timestamp cannot be parsed
  • "Token has expired" - Token’s expiration time has passed

Best Practices

Security Recommendations
  • Always validate tokens on the server side
  • Use HTTPS to prevent token interception
  • Store tokens securely (httpOnly cookies recommended)
  • Implement token refresh before expiration
  • Clear tokens on logout
  • Never expose token validation logic to clients
  • Rotate tokens after sensitive operations
Implementation Tips
  • Set appropriate expiration times based on security needs
  • Use shorter expirations (30m-1h) for web sessions
  • Use longer expirations (7d-30d) for API keys
  • Implement automatic token refresh for better UX
  • Warn users before token expiration
  • Log token validation failures for security monitoring
  • Consider using sliding window expiration

Performance Considerations

  • Token validation is very fast (string parsing and timestamp comparison)
  • Cache validation results if checking the same token multiple times
  • Consider using Redis or similar for distributed session validation
  • Implement rate limiting on token validation endpoints

Testing

import { isSessionTokenValid } from 'validauth';

// Generate test token with known timestamp
function generateTestToken(ageMs = 0) {
  const randomPart = 'a'.repeat(32);
  const timestamp = Date.now() - ageMs;
  return randomPart + timestamp;
}

// Test expired token
const expiredToken = generateTestToken(2 * 60 * 60 * 1000); // 2 hours old
const result = isSessionTokenValid(expiredToken, { expiresIn: '1h' });
console.log(result); // false

// Test valid token
const validToken = generateTestToken(30 * 60 * 1000); // 30 minutes old
const result2 = isSessionTokenValid(validToken, { expiresIn: '1h' });
console.log(result2); // true

Build docs developers (and LLMs) love