diff --git a/auth/auth.go b/auth/auth.go index 9cffaa2..98b2763 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -461,20 +461,17 @@ func getOAuth2Scopes() []string { } func openBrowser(url string) error { - var cmd string - var args []string + cmd, args := browserLaunchCommand(runtime.GOOS, url) + return exec.Command(cmd, args...).Start() +} - switch runtime.GOOS { +func browserLaunchCommand(goos, url string) (string, []string) { + switch goos { case "windows": - cmd = "cmd" - args = []string{"/c", "start", url} + return "rundll32", []string{"url.dll,FileProtocolHandler", url} case "darwin": - cmd = "open" - args = []string{url} + return "open", []string{url} default: - cmd = "xdg-open" - args = []string{url} + return "xdg-open", []string{url} } - - return exec.Command(cmd, args...).Start() } diff --git a/auth/auth_test.go b/auth/auth_test.go index 9faebbb..f8bc1e3 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -264,3 +264,28 @@ func TestGetOAuth2HeaderNoToken(t *testing.T) { token := tokenStore.GetOAuth2Token("nobody") assert.Nil(t, token) } + +func TestBrowserLaunchCommand(t *testing.T) { + url := "https://x.com/i/oauth2/authorize?client_id=abc&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&response_type=code&scope=tweet.read+users.read&state=123&code_challenge=xyz&code_challenge_method=S256" + + t.Run("windows keeps the full oauth url as a single argument", func(t *testing.T) { + cmd, args := browserLaunchCommand("windows", url) + + assert.Equal(t, "rundll32", cmd) + assert.Equal(t, []string{"url.dll,FileProtocolHandler", url}, args) + }) + + t.Run("darwin uses open", func(t *testing.T) { + cmd, args := browserLaunchCommand("darwin", url) + + assert.Equal(t, "open", cmd) + assert.Equal(t, []string{url}, args) + }) + + t.Run("linux uses xdg-open", func(t *testing.T) { + cmd, args := browserLaunchCommand("linux", url) + + assert.Equal(t, "xdg-open", cmd) + assert.Equal(t, []string{url}, args) + }) +} diff --git a/store/tokens.go b/store/tokens.go index ec5aebc..1420da1 100644 --- a/store/tokens.go +++ b/store/tokens.go @@ -83,6 +83,20 @@ type TokenStore struct { FilePath string `yaml:"-"` } +func resolveHomeDir() string { + if homeDir := os.Getenv("HOME"); homeDir != "" { + return homeDir + } + + homeDir, err := os.UserHomeDir() + if err != nil { + fmt.Println("Error getting home directory:", err) + return "." + } + + return homeDir +} + // Creates a new TokenStore, loading from ~/.xurl (auto-migrating legacy JSON). func NewTokenStore() *TokenStore { return NewTokenStoreWithCredentials("", "") @@ -92,12 +106,7 @@ func NewTokenStore() *TokenStore { // client credentials into any app that was migrated without them (i.e. legacy // JSON migration where CLIENT_ID / CLIENT_SECRET came from env vars). func NewTokenStoreWithCredentials(clientID, clientSecret string) *TokenStore { - homeDir, err := os.UserHomeDir() - if err != nil { - fmt.Println("Error getting home directory:", err) - homeDir = "." - } - + homeDir := resolveHomeDir() filePath := filepath.Join(homeDir, ".xurl") store := &TokenStore{