Files
github--github-mcp-server/internal/oauth/env.go
T
2026-07-13 12:37:57 +08:00

71 lines
2.2 KiB
Go

package oauth
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"runtime"
"strings"
)
// errNoDisplay reports that the host has no display server, so no browser can be
// launched. It is a definitive headless signal (unlike a generic launch error),
// which lets the flow prefer device authorization — the only channel reachable
// from a browser on another machine (e.g. a remote SSH session).
var errNoDisplay = errors.New("no display server detected")
// openBrowser tries to open url in the user's default browser. It returns an
// error when no browser can plausibly be launched so the caller can fall back
// to elicitation. On Linux it treats a headless session (no display server) as
// unopenable, which is the common case for SSH and containers.
func openBrowser(url string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "linux":
if os.Getenv("DISPLAY") == "" && os.Getenv("WAYLAND_DISPLAY") == "" {
return errNoDisplay
}
cmd = exec.Command("xdg-open", url)
case "darwin":
cmd = exec.Command("open", url)
case "windows":
cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", url)
default:
return fmt.Errorf("unsupported platform: %s", runtime.GOOS)
}
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
if err := cmd.Start(); err != nil {
return err
}
// The launcher (xdg-open/open/rundll32) exits as soon as it hands off to the
// browser. Reap it asynchronously so it does not linger as a zombie for the
// lifetime of this long-running server.
go func() { _ = cmd.Wait() }()
return nil
}
// isRunningInDocker reports whether the process is running inside a Docker (or
// containerd) container. Detection relies on Linux-specific paths and is always
// false elsewhere. It is used only to skip a PKCE flow that cannot work: a
// random callback port inside a container cannot be reached from the host
// browser, so we go straight to device flow in that case.
func isRunningInDocker() bool {
if runtime.GOOS != "linux" {
return false
}
if _, err := os.Stat("/.dockerenv"); err == nil {
return true
}
if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
s := string(data)
if strings.Contains(s, "docker") || strings.Contains(s, "containerd") {
return true
}
}
return false
}