Review: Register Notion OAuth app

review-1046-2026-04-21 Review

review needs-refinement

Verdict: NEEDS_REFINEMENT

Ticket: board item #1046 — forgejo_admin/notion-mcp-remote#6feat: register public Notion OAuth integration for remote proxy
Labels: type:feature, story:claude-ai-connect, arch:notion-oauth — points: 1 — column: backlog

Template Completeness

Validated against template-issue-feature:
  • [x] ### Type — Feature
  • [x] ### Lineage — standalone, scoped from project
  • [x] ### Repoforgejo_admin/notion-mcp-remote
  • [x] ### User Story — present (AS/I want/So that)
  • [x] ### Context — clear, explains three-legged flow and Public-vs-Internal distinction
  • [x] ### File Targets — present (explicitly "no repo file changes") and lists target secrets plus files NOT to touch
  • [x] ### Acceptance Criteria — 5 ACs, testable
  • [x] ### Test Expectations — manual browser smoke + curl smoke
  • [x] ### Constraints — three real constraints listed
  • [x] ### Checklist — present
  • [x] ### Related — project + story + arch

Traceability

  • [x] story:claude-ai-connect label — Connect Notion to claude.ai
  • [x] story note verified — entry exists in project-notion-mcp-remote user-stories section, linking to story-notion-mcp-remote-claude-ai-connect
  • [x] arch:notion-oauth label — OAuth handshake component
  • [ ] arch note MISSING under that exact slug — search_notes("arch-notion-oauth") returned no results. The OAuth handshake is documented, but under arch-dataflow-notion-mcp-remote (sequenceDiagram explicitly covering authorize/callback/token). [SCOPE] Either rename the label to arch:dataflow-notion-mcp-remote or create a dedicated arch-notion-oauth component note. Recommend re-labeling since the dataflow note already covers this material.
  • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote/issues/6, open, created 2026-04-21

File Targets

This is credential provisioning — no repo file edits expected. The ticket correctly states this and lists downstream consumers:
  • [x] ~/secrets/notion-mcp-remote/credentials.env — verified directory exists at ~/secrets/notion-mcp-remote/
  • [x] server.py:19-20 already reads NOTION_OAUTH_CLIENT_ID / NOTION_OAUTH_CLIENT_SECRET from env — verified via Grep
  • [x] .env.example:2-3 uses the same two key names — verified
  • [x] k8s/deployment.yaml:43-52 mounts both from secret notion-mcp-secrets keys oauth-client-id / oauth-client-secret — verified
  • [x] "Do not touch" list is accurate — server.py and requirements.txt correctly already consume these env vars

Repo Placement

OK — the issue is filed on forgejo_admin/notion-mcp-remote and the credentials feed this service's secret. Sibling ticket #7 (k8s secret provisioning) and #8 (Woodpecker activation) are correctly split into separate issues because they touch different systems.

Dependencies

Board scan (board-notion-mcp-remote):
  • This ticket blocks:
  • Not blocked by anything in in_progress — safe to pull.
  • Dependencies implied in context ("Blocks ticket #5 / #8") per router brief do not match the actual board — ticket #5 is the Harbor/Woodpecker CI bug, and #8 is Woodpecker repo activation, neither of which consume Notion OAuth credentials directly. [BODY] Clarify blocker chain in the issue body: this ticket unblocks #7 (secret provisioning) and #9 (E2E verify), not #5 and #8. (Or correct the router brief — this is a router-context issue, not a ticket-body issue, so noting for Ava.)

Acceptance Criteria

All 5 ACs are verifiable by an agent/human:
  • Integration exists in Daily workspace — verifiable by loading the Notion integrations UI.
  • Distribution is Public — same UI verification.
  • Redirect URI exact — ISSUE: wrong path. The ticket specifies https://notion-mcp-remote.tail5b443a.ts.net/callback, but the running service advertises and consumes /oauth/callback (confirmed in mcp-remote-auth/src/mcp_remote_auth/routes.py:41 and provider.py:125,151). Using /callback will cause Notion to reject the token exchange. [BODY] Fix redirect URI to https://notion-mcp-remote.tail5b443a.ts.net/oauth/callback in the AC, the smoke curl, and the Constraints section.
  • Creds captured in ~/secrets/notion-mcp-remote/credentials.env — testable by file presence + key names.
  • Smoke curl returns consent HTML — valid once redirect URI is fixed.
[BODY] Optional consideration: the service also exposes /onboard/callback (onboarding flow, onboarding.py:136). Out of scope for the claude-ai-connect story, but if Lucas plans to use the same public integration for onboarding, that redirect URI should also be registered now to avoid a second manual trip to the Notion UI.

Blast Radius

  • Credential leakage: Constraint already calls out "do not commit CLIENT_SECRET to any git repo." Good.
  • Redirect URI mismatch: If the wrong path (/callback) is configured with Notion, the entire OAuth leg fails silently at the token exchange. The ACs as written would be marked green by an agent but the service would still be broken end-to-end — #9 (E2E verify) would catch it, but a lot of work happens in between. Fixing AC #3 prevents this.
  • Base URL drift: BASE_URL env var is the single source of truth; if the Tailscale Funnel hostname ever changes, the Notion integration redirect URI must be updated in the Notion UI. Worth a note in Constraints.

Decomposition Assessment

1 point, 5 ACs, no code edits, one UI flow. Well within the 5-minute rule. No decomposition needed.

Recommendation

Two concrete fixes required before this can move to todo:
  • [BODY] Fix the redirect URI path throughout the issue (AC #3, smoke curl URL in AC #5, Constraints, Test Expectations): /callback/oauth/callback. This is the critical fix — the current path will silently break the handshake.
  • [SCOPE] Resolve the arch:notion-oauth label: either (a) change the label to arch:dataflow-notion-mcp-remote to match the existing architecture note, or (b) create a dedicated arch-notion-oauth component note. Option (a) is lighter weight and the existing note already captures the handshake.
Optional (non-blocking):
  • [BODY] Add /onboard/callback as a second registered redirect URI if the same integration will back onboarding (future story).
  • [BODY] Correct the blocker references in the ticket's lineage/related context (blocks #7 and #9, not #5 and #8) — or clarify router brief.