Skip to content

Make OAuth session token refresh functional end-to-end #146

Description

@getlarge

Problem

The current OAuth session refresh support looks close to complete, but the full flow is not functional end to end. The pieces exist, but they are not wired together in a way that makes human MCP sessions reliably refresh across normal use and server restarts.

What exists today

  • createSessionAuthPreHandler() contains session-aware token lookup and opportunistic refresh logic.
  • registerTokenRefreshService() exists and starts automatically when registered.
  • refreshSessionToken(sessionId) is implemented with concrete refresh + session update + notification behavior.
  • Redis and memory session stores both have tokenRefresh and token-hash mapping support.

What is missing / inconsistent

1. The plugin still wires the generic prehandler, not the session-aware one

In the main MCP plugin entrypoint, the auth path uses createAuthPreHandler(...), not createSessionAuthPreHandler(...). That means the session store and refresh metadata are not part of the normal authenticated request path.

2. session-auth-prehandler.ts is behind prehandler.ts

createSessionAuthPreHandler() does not currently match the newer behavior in createAuthPreHandler(). At minimum it appears to be missing parity for:

  • /mcp/.well-known skip behavior
  • /oauth/callback and /oauth/register skip behavior
  • config.excludedPaths handling

So replacing the generic handler with the session-aware one today would regress behavior.

3. The OAuth callback path does not appear to persist refresh metadata into the session

The authorization callback returns or redirects with tokens, but the session-aware refresh path depends on session.tokenRefresh already being present in the session store. Without persisting refresh metadata during callback/session establishment, later requests can link a bearer token to an MCP session but still have nothing to refresh with.

4. The background refresh service is not integrated into the main plugin path

registerTokenRefreshService() exists, but the main MCP plugin path does not appear to register it.

5. The periodic refresh loop is still a stub

performTokenRefresh() is still explicitly a placeholder. The service framework and the per-session refresh method are real, but the automatic periodic sweep over expiring sessions is not implemented.

Why this matters

For human MCP clients using OAuth authorization code flow, this creates an awkward middle state where the codebase suggests session refresh support, but in practice the end-to-end path is not dependable. In particular:

  • session-aware refresh is not active by default
  • refresh metadata may never be persisted
  • app restarts drop in-memory session state
  • the background service cannot currently rescue expiring sessions automatically

Proposed direction

Treat this as one feature that needs to be completed end to end:

  1. Bring createSessionAuthPreHandler() to feature parity with createAuthPreHandler().
  2. Decide whether the session-aware handler should replace the generic one when OAuth session support is enabled.
  3. Persist tokenRefresh and related auth/session state during the OAuth callback/session establishment flow.
  4. Register the token refresh service from the main plugin path when the required pieces are available.
  5. Implement performTokenRefresh() so the periodic service actually refreshes expiring sessions.
  6. Add coverage for the full flow, including restart/persistence behavior with Redis-backed session storage.

Expected outcome

A user who authenticates via OAuth authorization code flow should be able to:

  • establish an MCP session
  • have refresh metadata stored with that session
  • continue using the session as access tokens expire
  • survive normal server restarts when using Redis-backed session storage

Right now the building blocks are present, but the feature is not complete as a system.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions