Skip to content

OAuth re-authentication loop after double-401: authorization code delivered to localhost:8033 but POST /token is never called #256

@ashishprasher

Description

@ashishprasher

Summary

When both the access token and refresh token are expired simultaneously
(e.g. after 30 days of inactivity, or when both are deleted server-side),
mcp-remote enters an infinite re-authentication loop. The user successfully
completes OAuth login and the authorization code is delivered to the
localhost:8033 callback server — but mcp-remote never calls POST /token
to exchange it. No new tokens are issued. The loop continues indefinitely
without restarting Claude Desktop.

Reproduction Steps

  1. Connect Claude Desktop to a remote MCP server using mcp-remote (OAuth flow)
  2. Authenticate successfully — confirm tools are working
  3. Delete both the access token and refresh token from the server's token
    store (simulating natural expiry after 30 days of inactivity)
  4. Do NOT restart Claude Desktop
  5. Ask Claude to run any tool
  6. The OAuth login screen opens — fill in credentials and complete login
  7. The browser shows the success page / redirects to localhost:8033

Result: mcp-remote loops back to the OAuth screen. Repeating step 6 any
number of times does not fix it.

Expected: After completing login, mcp-remote exchanges the authorization
code for new tokens and subsequent tool calls succeed.

Server-Side Evidence

With full logging enabled on the OAuth server, after handleSuCallback
redirects to localhost:8033/oauth/callback?code=...:

  • challengeForAuthorizationCode() is never called
  • exchangeAuthorizationCode() is never called
  • POST /token never arrives at the server
  • mcp-remote immediately retries with the old expired token

This confirms the authorization code is received at localhost:8033
(the redirect succeeds) but mcp-remote never processes it into a token
exchange. The promise waiting for the auth code does not resolve.

Server Log Pattern

[OAuth] verifyAccessToken() — token NOT FOUND: d205778f...
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 3beeb7e4...
[OAuth] handleAuthorizeStart() — session lookup: FOUND
[OAuth] handleSuCallback() — SU token exchange SUCCESS
[OAuth] handleSuCallback() — MCP auth code generated, redirecting to: http://localhost:8033/oauth/ca...

← no challengeForAuthorizationCode, no exchangeAuthorizationCode, no POST /token

[OAuth] verifyAccessToken() — token NOT FOUND: d205778f... ← same old token
[OAuth] exchangeRefreshToken() — refresh token NOT FOUND
[OAuth] authorize() — created sessionId: 446c62d9... ← loop

Workaround

Restarting Claude Desktop fully (Cmd+Q → reopen) resets mcp-remote's
internal state. The next OAuth flow completes correctly including the
token exchange.

Environment

  • mcp-remote: 0.1.38
  • Claude Desktop, macOS
  • Triggered by: both access token + refresh token expired simultaneously

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions