Skip to content

[Security follow-up C-4] Add MCP token expiration + rotation #45

Description

@hoainho

🌟 Before claiming this issue

Two quick steps before you open a PR:

  1. ⭐ Star the repository — low-friction signal that you'll follow through
  2. 💬 Comment I'll take this (or similar) below — prevents two contributors racing on the same issue

Full policy: CONTRIBUTING.md → How to claim. If a claim is older than 7 days with no PR, you can reclaim it politely.


Tracks follow-up #4 from PR #17 security review. Blocked on mcp-server-v1 M3 (~Jul 28, 2026).

What

MCP pairing tokens currently never expire. Add token expiration (e.g., 24-hour TTL) + a rotation mechanism so users can revoke + re-pair.

Why

A pairing token grants full debugger control. If a user pairs from a shared machine, leaves the browser open, then a roommate uses the laptop, the token persists indefinitely. Even on personal machines: tokens leak via screen-recording, screen-share, OS-level keylogger, etc. A 24h TTL bounds the exposure window.

Rotation lets a user "rotate" without going through the full pairing handshake — useful after the user notices suspicious activity in the panel.

Acceptance criteria

  • Tokens written to storage include a { token: string; expiresAt: number } shape (not just the raw string)
  • readToken() returns null if expiresAt < Date.now() AND deletes the expired entry
  • Settings UI has a "Rotate pairing token" button (calls server's rotation endpoint, replaces stored token)
  • CHANGELOG: document the 24h TTL default + how to rotate
  • Unit test: write token with expiresAt: Date.now() - 1000, then readToken() returns null and chrome.storage.session.get() shows no entry remains

Implementation hint

const TOKEN_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours

type StoredToken = { token: string; expiresAt: number };

async function saveToken(token: string) {
  const entry: StoredToken = {
    token,
    expiresAt: Date.now() + TOKEN_TTL_MS,
  };
  await chrome.storage.session.set({ [TOKEN_KEY]: entry });
}

async function readToken(): Promise<string | null> {
  const { [TOKEN_KEY]: entry } = await chrome.storage.session.get(TOKEN_KEY);
  if (!entry) return null;
  if ((entry as StoredToken).expiresAt < Date.now()) {
    await chrome.storage.session.remove(TOKEN_KEY);
    return null;
  }
  return (entry as StoredToken).token;
}

async function rotateToken() {
  const newToken = await mcpServer.rotateToken(); // hits the server's rotate endpoint
  await saveToken(newToken);
}

Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededsecuritySecurity-related (see SECURITY.md for vulnerabilities)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions