claude-code - 💡(How to fix) Fix [BUG] Notion MCP OAuth: complete_authentication fails when mcp.notion.com relay returns callback instead of localhost [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52936Fetched 2026-04-25 06:16:46
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
3
Author
Timeline (top)
labeled ×4renamed ×2closed ×1commented ×1

Error Message

When authenticating the Notion MCP server (https://mcp.notion.com/mcp), the browser ends up at mcp.notion.com/callback instead of being forwarded to the local Claude Code callback server. Passing the mcp.notion.com/callback URL to mcp__notion__complete_authentication (the documented fallback) fails with a state mismatch error, making it impossible to complete authentication.

Error Messages/Logs

  1. Error: Authentication failed for notion: OAuth state mismatch - possible CSRF attack The mcp__notion__complete_authentication tool documentation states it handles the case where "the browser shows a connection error on the redirect page." However, when using Notion MCP, the browser never reaches localhost at all — it stays at mcp.notion.com/callback. The tool needs to handle this relay pattern.

Root Cause

Root cause: mcp.notion.com wraps the original state string inside a base64-encoded JSON object before sending the callback:

{
  "state": "<original-raw-state>",
  "redirectUri": "http://localhost:<port>/callback",
  "codeChallenge": "...",
  ...
}

complete_authentication compares the raw state query parameter against the stored state, but it receives the base64 JSON wrapper instead of the raw state — so validation fails even though the original state is present inside the JSON.

Code Example

Authentication failed for notion: OAuth state mismatch - possible CSRF attack

---

{
  "state": "<original-raw-state>",
  "redirectUri": "http://localhost:<port>/callback",
  "codeChallenge": "...",
  ...
}
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

When authenticating the Notion MCP server (https://mcp.notion.com/mcp), the browser ends up at mcp.notion.com/callback instead of being forwarded to the local Claude Code callback server. Passing the mcp.notion.com/callback URL to mcp__notion__complete_authentication (the documented fallback) fails with a state mismatch error, making it impossible to complete authentication.

What Should Happen?

mcp__notion__complete_authentication should accept https://mcp.notion.com/callback?code=...&state=<base64-JSON> as a valid callback URL by decoding the state JSON and validating the inner state field against the stored value.

Error Messages/Logs

Authentication failed for notion: OAuth state mismatch - possible CSRF attack

Steps to Reproduce

  1. Call mcp__notion__authenticate — receive an auth URL with redirect_uri=http://localhost:<port>/callback
  2. Open the URL in browser → mcp.notion.com redirects to https://www.notion.so/install-integration?...&redirect_uri=https://mcp.notion.com/callback&...
  3. Authorize on the Notion page
  4. Browser lands on https://mcp.notion.com/callback?code=...&state=<base64-JSON> — the relay does not forward to localhost
  5. Paste that URL into mcp__notion__complete_authentication
  6. Error: Authentication failed for notion: OAuth state mismatch - possible CSRF attack

Root cause: mcp.notion.com wraps the original state string inside a base64-encoded JSON object before sending the callback:

{
  "state": "<original-raw-state>",
  "redirectUri": "http://localhost:<port>/callback",
  "codeChallenge": "...",
  ...
}

complete_authentication compares the raw state query parameter against the stored state, but it receives the base64 JSON wrapper instead of the raw state — so validation fails even though the original state is present inside the JSON.

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

N/A

Claude Code Version

2.1.119 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

The mcp__notion__complete_authentication tool documentation states it handles the case where "the browser shows a connection error on the redirect page." However, when using Notion MCP, the browser never reaches localhost at all — it stays at mcp.notion.com/callback. The tool needs to handle this relay pattern.

extent analysis

TL;DR

Update mcp__notion__complete_authentication to decode the base64 JSON state and validate the inner state field.

Guidance

  • Verify that the mcp__notion__complete_authentication function is correctly handling the base64 JSON wrapper by checking its documentation and implementation.
  • Modify the mcp__notion__complete_authentication function to decode the base64 JSON state and extract the original state string for validation.
  • Test the updated function with the provided callback URL to ensure it correctly validates the state and completes authentication.
  • Consider adding error handling for cases where the base64 JSON wrapper is missing or malformed.

Example

import base64
import json

def decode_state(state):
    try:
        json_state = base64.b64decode(state).decode('utf-8')
        return json.loads(json_state)['state']
    except (base64.binascii.Error, json.JSONDecodeError):
        return None

# Usage:
state = '<base64-JSON>'
original_state = decode_state(state)
if original_state:
    # Validate the original state
    pass

Notes

The provided root cause analysis suggests that the issue is due to the base64 JSON wrapper added by mcp.notion.com. Updating the mcp__notion__complete_authentication function to handle this wrapper should resolve the issue.

Recommendation

Apply workaround: Update the mcp__notion__complete_authentication function to handle the base64 JSON state wrapper, as this is a specific issue with the Notion MCP server and not a general bug in the Claude Code.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Notion MCP OAuth: complete_authentication fails when mcp.notion.com relay returns callback instead of localhost [1 comments, 2 participants]