Skip to content

feat: add custom scheme to handle cursor and other native tools - #31

Open
ar4s wants to merge 1 commit into
tuannvm:mainfrom
ar4s:feat/30-add-cursor-scheme
Open

ar4s wants to merge 1 commit into
tuannvm:mainfrom
ar4s:feat/30-add-cursor-scheme

Conversation

@ar4s

@ar4s ar4s commented May 12, 2026 •

Copy link
Copy Markdown

Summary

The redirect URI validation rejects non-HTTP(S) schemes, which breaks native desktop OAuth clients like Cursor that use custom callback URIs (e.g. cursor://anysphere.cursor-mcp/oauth/callback). Custom URI schemes are standard for native applications per RFC 8252.

Changes

  • New config option AllowedClientRedirectSchemes - comma-separated list of custom URI schemes to accept (e.g. "cursor,vscode"). Defaults to empty (no custom schemes allowed), preserving backward compatibility.
  • HandleAuthorize - replaced hard-coded http/https scheme check with isAllowedScheme() helper that also accepts configured custom schemes.

Usage

cfg := &oauth.Config{
    AllowedClientRedirectSchemes: "cursor,vscode",
    // ...
}

Or via environment:

OAUTH_ALLOWED_CLIENT_REDIRECT_SCHEMES=cursor,vscode

Testing

  • Tests pass locally
  • New tests added (if applicable)

Related Issues

Fixes #30

Summary by CodeRabbit

  • New Features

    • Add support for configurable custom OAuth client redirect URI schemes (e.g., cursor://, vscode://) per RFC 8252.
  • Security / Bug Fixes

    • Redirect validation now only accepts explicitly allowed custom schemes while retaining HTTPS enforcement for standard redirects and localhost rules.
  • Documentation

    • Configuration and security docs updated with guidance, examples, and how to configure allowed custom schemes.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 12, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ec4df8c-de45-40d7-a316-15d2f7431211

📥 Commits

Reviewing files that changed from the base of the PR and between 98f7c86 and 26ca745.

📒 Files selected for processing (7)
  • config.go
  • config_test.go
  • docs/CONFIGURATION.md
  • docs/SECURITY.md
  • fixed_redirect_test.go
  • handlers.go
  • security_test.go
✅ Files skipped from review due to trivial changes (1)
  • docs/CONFIGURATION.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • config_test.go
  • config.go
  • security_test.go
  • handlers.go
  • fixed_redirect_test.go

Walkthrough

Adds RFC 8252-compliant custom URI scheme support for native application OAuth redirects. Configuration introduces AllowedClientRedirectSchemes and env wiring. Handler validation now uses a centralized allowlist to accept configured custom schemes alongside standard HTTP(S) domain/HTTPS checks. Tests and docs updated accordingly.

Changes

Custom URI Scheme Allowlist

Layer / File(s) Summary
Configuration contract and environment wiring
config.go, config_test.go
Adds AllowedClientRedirectSchemes to Config, WithAllowedClientRedirectSchemes() builder, and FromEnv() support for OAUTH_ALLOWED_CLIENT_REDIRECT_SCHEMES. Tests validate builder acceptance of comma-separated schemes.
Handler validation logic and custom scheme allowlist
handlers.go
Adds OAuth2Config.AllowedClientRedirectSchemes, maps it from Config, refactors isAllowedClientRedirectURI() to parse URIs earlier, and introduces isAllowedScheme() / isCustomScheme() helpers. HandleAuthorize uses the allowlist to permit configured custom schemes while preserving HTTPS/domain checks for standard schemes.
Security and fixed-redirect test coverage
security_test.go, fixed_redirect_test.go, config_test.go
Expands TestIsAllowedClientRedirectURI with custom-scheme cases (configured vs not configured, multi-scheme, case-insensitive). Adds TestFixedRedirectModeCustomSchemes and updates related test labels and logs.
Configuration and security documentation
docs/CONFIGURATION.md, docs/SECURITY.md
Documents AllowedClientRedirectSchemes, the OAUTH_ALLOWED_CLIENT_REDIRECT_SCHEMES env var, and adds a "Custom URI Schemes (RFC 8252)" security section with examples and considerations.

Sequence Diagram

sequenceDiagram
  participant Client as Native App Client
  participant Handler as OAuth2Handler
  participant Validator as isAllowedClientRedirectURI
  participant Checker as isAllowedScheme
  participant Config as AllowedClientRedirectSchemes

  Client->>Handler: HandleAuthorize(redirect_uri)
  Handler->>Validator: isAllowedClientRedirectURI(redirect_uri)
  Validator->>Validator: Parse URI (scheme, host)
  Validator->>Checker: isAllowedScheme(scheme)
  Checker->>Config: lookup scheme in allowlist
  Config-->>Checker: true/false
  Checker-->>Validator: allowed/rejected
  alt allowed
    Validator-->>Handler: accept
    Handler-->>Client: proceed with auth
  else rejected
    Validator-->>Handler: reject
    Handler-->>Client: error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tuannvm/oauth-mcp-proxy#21: Modifies redirect-URI validation and related handlers; related because both PRs change allowlisting/validation for client redirect URIs.

Poem

🐰 A rabbit hops through custom schemes,
cursor:// and vscode:// in dreams,
RFC 8252 now lights the way,
Native apps can redirect today,
thump thump 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding support for custom URI schemes (like cursor) for native tools, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR successfully implements the requirements from issue #30: it adds configurable custom redirect URI schemes via AllowedClientRedirectSchemes, updates validation logic to accept configured custom schemes, and provides both programmatic and environment variable configuration options.
Out of Scope Changes check ✅ Passed All code changes are directly related to implementing custom redirect URI scheme support as specified in issue #30, with no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ar4s
ar4s force-pushed the feat/30-add-cursor-scheme branch from f164a23 to 98f7c86 Compare May 12, 2026 16:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
handlers.go (1)

925-938: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localhost short-circuit bypasses custom-scheme allowlist.

isAllowedClientRedirectURI accepts custom://localhost/... even when the scheme is not configured, because localhost is checked before scheme policy. This weakens the callback re-validation path.

Proposed fix
 func (h *OAuth2Handler) isAllowedClientRedirectURI(uri string) bool {
-	// Always allow localhost URIs for development tools
-	if isLocalhostURI(uri) {
-		return true
-	}
-
 	parsedURI, err := url.Parse(uri)
 	if err != nil {
 		return false
 	}
 
 	// Allow explicitly configured custom schemes (e.g. cursor://, vscode://) per RFC 8252
 	if h.isCustomScheme(parsedURI.Scheme) {
 		return true
 	}
+
+	// Allow localhost only for standard loopback web redirects
+	if (parsedURI.Scheme == "http" || parsedURI.Scheme == "https") && isLocalhostURI(uri) {
+		return true
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@handlers.go` around lines 925 - 938, The localhost short-circuit in
isAllowedClientRedirectURI runs before scheme validation, allowing
custom://localhost/... to bypass the custom-scheme allowlist; fix by parsing the
URI first, then check h.isCustomScheme(parsedURI.Scheme) and return true if
allowed, and only then apply isLocalhostURI for non-custom/standard schemes
(e.g., restrict localhost allowance to http/https or when the scheme is
explicitly allowed). Update the logic in isAllowedClientRedirectURI to call
url.Parse(uri) first, use parsedURI.Scheme with h.isCustomScheme(...), and only
treat isLocalhostURI(parsedURI) as a fallback for standard schemes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/SECURITY.md`:
- Around line 403-408: The fenced code block containing the three lines
beginning with "ok:  cursor://anysphere.cursor-mcp/oauth/callback" should
include a language tag to satisfy markdown lint (MD040); update the opening
fence from ``` to ```text so the block (the lines with "ok:  cursor://..." and
"bad: cursor://...") is explicitly marked as plain text.

---

Outside diff comments:
In `@handlers.go`:
- Around line 925-938: The localhost short-circuit in isAllowedClientRedirectURI
runs before scheme validation, allowing custom://localhost/... to bypass the
custom-scheme allowlist; fix by parsing the URI first, then check
h.isCustomScheme(parsedURI.Scheme) and return true if allowed, and only then
apply isLocalhostURI for non-custom/standard schemes (e.g., restrict localhost
allowance to http/https or when the scheme is explicitly allowed). Update the
logic in isAllowedClientRedirectURI to call url.Parse(uri) first, use
parsedURI.Scheme with h.isCustomScheme(...), and only treat
isLocalhostURI(parsedURI) as a fallback for standard schemes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9c05b4b6-42b9-42ff-a276-fae6f7202daa

📥 Commits

Reviewing files that changed from the base of the PR and between ba543d8 and f164a23.

📒 Files selected for processing (7)
  • config.go
  • config_test.go
  • docs/CONFIGURATION.md
  • docs/SECURITY.md
  • fixed_redirect_test.go
  • handlers.go
  • security_test.go

Comment thread docs/SECURITY.md
Comment on lines +403 to +408
```
ok: cursor://anysphere.cursor-mcp/oauth/callback (when "cursor" configured)
ok: vscode://vscode.github-authentication/callback (when "vscode" configured)
bad: cursor://anysphere.cursor-mcp/oauth/callback (when not configured)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced code block.

The block starting at Line 403 has no language identifier (MD040), which can break markdown lint checks.

Proposed fix
-```
+```text
 ok:  cursor://anysphere.cursor-mcp/oauth/callback  (when "cursor" configured)
 ok:  vscode://vscode.github-authentication/callback (when "vscode" configured)
 bad: cursor://anysphere.cursor-mcp/oauth/callback   (when not configured)
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 403-403: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/SECURITY.md` around lines 403 - 408, The fenced code block containing
the three lines beginning with "ok: 
cursor://anysphere.cursor-mcp/oauth/callback" should include a language tag to
satisfy markdown lint (MD040); update the opening fence from ``` to ```text so
the block (the lines with "ok:  cursor://..." and "bad: cursor://...") is
explicitly marked as plain text.

@ar4s
ar4s force-pushed the feat/30-add-cursor-scheme branch from 98f7c86 to 26ca745 Compare May 13, 2026 10:02
@ar4s

ar4s commented May 15, 2026

Copy link
Copy Markdown
Author

Hi @tuannvm - just following up on this PR when you have time. I believe it’s ready for review, and I’d appreciate any feedback. Thanks!

@mkjmdski

Copy link
Copy Markdown

That would be very useful!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support native-app OAuth redirect URIs (e.g. cursor://) for MCP clients

2 participants