From 6588688ae5836180b76b0ee8ef32f5211abac5cd Mon Sep 17 00:00:00 2001 From: Puneet Punamiya Date: Tue, 11 Aug 2026 10:26:37 +0530 Subject: [PATCH] Fix Cursor OAuth callback handoff hanging on "waiting for callback" The post-login redirect page used a meta-refresh to hand the browser off to the MCP client's redirect_uri (Cursor's cursor:// deep link or its localhost:8787 loopback). Meta-refresh is unreliable for custom URL schemes, so the handoff would silently fail and Cursor would sit on "waiting for callback" even though the server-side SSO login had already succeeded. Replace it with an html/template-rendered page that does a JS redirect plus a visible fallback link, with the target URL typed as template.URL (a plain string field would get replaced with html/template's inert "#ZgotmplZ" placeholder for the cursor:// scheme, since it isn't on the default safe-scheme allowlist) and safely JSON-quoted for the inline ` @@ -40,26 +46,65 @@ const callbackPageTmpl = ` Authentication Successful -

Authentication complete

-

You may now close this window.

+

Returning to Cursor…

+

If Cursor stays on “waiting for callback”, click:

+

Open Cursor callback

+ ` +type redirectPageData struct { + Message string + // URL is typed template.URL (rather than string) because html/template + // otherwise sanitizes href values against a scheme allowlist that does + // NOT include custom schemes like cursor:// — an untyped string would + // silently render as the inert placeholder "#ZgotmplZ" instead of the + // real link. Callers must only pass already-validated redirect URIs + // (see isValidRedirectURI / HandleRegister's scheme checks). + URL template.URL + URLJS template.JS +} + func writeRedirectPage(w http.ResponseWriter, targetURL, message string) { + writeHTMLTemplate(w, redirectPageTmpl, redirectPageData{ + Message: message, + URL: template.URL(targetURL), + URLJS: jsString(targetURL), + }) +} + +func writeCallbackPage(w http.ResponseWriter, redirectURL string) { + writeHTMLTemplate(w, callbackPageTmpl, redirectPageData{ + URL: template.URL(redirectURL), + URLJS: jsString(redirectURL), + }) +} + +func writeHTMLTemplate(w http.ResponseWriter, tmpl string, data redirectPageData) { w.Header().Set("Content-Type", "text/html; charset=utf-8") - if _, err := fmt.Fprintf(w, redirectPageTmpl, message, targetURL, message); err != nil { + t, err := template.New("page").Parse(tmpl) + if err != nil { + slog.Error("failed to parse redirect page template", "error", err) + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + if err := t.Execute(w, data); err != nil { slog.Error("failed to write redirect page", "error", err) } } -func writeCallbackPage(w http.ResponseWriter, redirectURL string) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - if _, err := fmt.Fprintf(w, callbackPageTmpl, redirectURL); err != nil { - slog.Error("failed to write callback page", "error", err) +// jsString returns s as a safely-quoted JS string literal for embedding +// directly inside a