Files
goclaw/cmd/auth.go
T
Luan VuandGitHub 7d744eb4f2 refactor(oauth): DB-backed token storage, split codex.go, remove file-based artifacts (#65)
Replace file-based OAuth token storage with DB-backed storage using
llm_providers (access token) + config_secrets (refresh token).

- Store: Add Settings JSONB field, chatgpt_oauth provider type
- OAuth: DBTokenSource backed by provider + secrets stores
- HTTP: oauth.go uses DB stores + registers provider in-memory
- Providers: chatgpt_oauth support in registerInMemory/registerProvidersFromDB
- Config: Remove HasOAuthToken, revert envFallback→envStr
- CLI: auth commands call HTTP API on running gateway
- Split codex.go (478→189 LOC) into codex.go + codex_build.go + codex_types.go
- Frontend: Remove fake OAUTH_PROVIDER_ID, use real DB-backed providers
- Tests: Rewrite with mock stores, fix SSE mock servers
2026-03-07 00:15:30 +07:00

124 lines
3.1 KiB
Go

package cmd
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"github.com/spf13/cobra"
)
func authCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "auth",
Short: "Authenticate with LLM providers via OAuth",
Long: "Manage OAuth authentication via the running gateway. Requires the gateway to be running.",
}
cmd.AddCommand(authStatusCmd())
cmd.AddCommand(authLogoutCmd())
return cmd
}
// gatewayURL returns the base URL for the running gateway.
func gatewayURL() string {
if u := os.Getenv("GOCLAW_GATEWAY_URL"); u != "" {
return strings.TrimRight(u, "/")
}
host := os.Getenv("GOCLAW_HOST")
if host == "" {
host = "127.0.0.1"
}
port := os.Getenv("GOCLAW_PORT")
if port == "" {
port = "3577"
}
return fmt.Sprintf("http://%s:%s", host, port)
}
// gatewayRequest sends an authenticated request to the running gateway.
func gatewayRequest(method, path string) (map[string]interface{}, error) {
url := gatewayURL() + path
req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
if token := os.Getenv("GOCLAW_TOKEN"); token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("cannot reach gateway at %s: %w", gatewayURL(), err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("invalid response from gateway: %s", string(body))
}
if resp.StatusCode >= 400 {
if msg, ok := result["error"].(string); ok {
return nil, fmt.Errorf("gateway error: %s", msg)
}
return nil, fmt.Errorf("gateway returned status %d", resp.StatusCode)
}
return result, nil
}
func authStatusCmd() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Show OAuth authentication status",
Long: "Check if ChatGPT OAuth is configured on the running gateway.",
RunE: func(cmd *cobra.Command, args []string) error {
result, err := gatewayRequest("GET", "/v1/auth/openai/status")
if err != nil {
return err
}
if auth, _ := result["authenticated"].(bool); auth {
name, _ := result["provider_name"].(string)
fmt.Printf("OpenAI OAuth: active (provider: %s)\n", name)
fmt.Println("Use model prefix 'openai-codex/' in agent config (e.g. openai-codex/gpt-4o).")
} else {
fmt.Println("No OAuth tokens found.")
fmt.Println("Use the web UI to authenticate with ChatGPT OAuth.")
}
return nil
},
}
}
func authLogoutCmd() *cobra.Command {
return &cobra.Command{
Use: "logout [provider]",
Short: "Remove stored OAuth tokens",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
provider := "openai"
if len(args) > 0 {
provider = args[0]
}
if provider != "openai" {
return fmt.Errorf("unknown provider: %s (supported: openai)", provider)
}
_, err := gatewayRequest("POST", "/v1/auth/openai/logout")
if err != nil {
return err
}
fmt.Println("OpenAI OAuth token removed.")
return nil
},
}
}