Skip to content

Commit 9f32c51

Browse files
committed
Add ChatGPT Plus/Pro OAuth login support
Add 'chatgpt' as a provider alias so users with a ChatGPT Plus/Pro subscription can use models like chatgpt/o3 without a separate API key. Authentication uses the OAuth2 PKCE flow against auth.openai.com (the same flow as the Codex CLI). After obtaining OAuth tokens, the id_token is exchanged for a standard OpenAI API key via token exchange. Tokens are persisted to ~/.config/cagent/chatgpt_token.json and automatically refreshed when expired. New CLI commands: cagent login chatgpt - authenticate via browser cagent logout chatgpt - remove stored credentials New package pkg/chatgpt with auth, token storage, and an environment.Provider that resolves CHATGPT_ACCESS_TOKEN. Assisted-By: cagent
1 parent 5077236 commit 9f32c51

8 files changed

Lines changed: 1018 additions & 3 deletions

File tree

cmd/root/login.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package root
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/docker/cagent/pkg/chatgpt"
9+
)
10+
11+
func newLoginCmd() *cobra.Command {
12+
cmd := &cobra.Command{
13+
Use: "login <provider>",
14+
Short: "Authenticate with a model provider",
15+
Long: "Authenticate with a model provider using OAuth. Currently supports 'chatgpt' for ChatGPT Plus/Pro subscriptions.",
16+
GroupID: "core",
17+
Example: ` cagent login chatgpt`,
18+
Args: cobra.ExactArgs(1),
19+
RunE: func(cmd *cobra.Command, args []string) error {
20+
provider := args[0]
21+
switch provider {
22+
case "chatgpt":
23+
return loginChatGPT(cmd)
24+
default:
25+
return fmt.Errorf("unsupported provider %q (supported: chatgpt)", provider)
26+
}
27+
},
28+
}
29+
30+
return cmd
31+
}
32+
33+
func loginChatGPT(cmd *cobra.Command) error {
34+
fmt.Fprintln(cmd.OutOrStdout(), "Opening browser to authenticate with ChatGPT...")
35+
36+
token, err := chatgpt.Login(cmd.Context())
37+
if err != nil {
38+
return fmt.Errorf("ChatGPT login failed: %w", err)
39+
}
40+
41+
if err := chatgpt.SaveToken(token); err != nil {
42+
return fmt.Errorf("failed to save token: %w", err)
43+
}
44+
45+
fmt.Fprintln(cmd.OutOrStdout(), "Successfully authenticated with ChatGPT!")
46+
fmt.Fprintln(cmd.OutOrStdout(), "You can now use 'chatgpt' as a provider, e.g.: chatgpt/o3")
47+
return nil
48+
}
49+
50+
func newLogoutCmd() *cobra.Command {
51+
cmd := &cobra.Command{
52+
Use: "logout <provider>",
53+
Short: "Remove stored authentication for a provider",
54+
Long: "Remove stored authentication tokens for a model provider.",
55+
GroupID: "core",
56+
Example: ` cagent logout chatgpt`,
57+
Args: cobra.ExactArgs(1),
58+
RunE: func(cmd *cobra.Command, args []string) error {
59+
provider := args[0]
60+
switch provider {
61+
case "chatgpt":
62+
if err := chatgpt.RemoveToken(); err != nil {
63+
return fmt.Errorf("failed to remove ChatGPT token: %w", err)
64+
}
65+
fmt.Fprintln(cmd.OutOrStdout(), "Successfully logged out from ChatGPT.")
66+
return nil
67+
default:
68+
return fmt.Errorf("unsupported provider %q (supported: chatgpt)", provider)
69+
}
70+
},
71+
}
72+
73+
return cmd
74+
}

cmd/root/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ func NewRootCmd() *cobra.Command {
105105
cmd.AddCommand(newDebugCmd())
106106
cmd.AddCommand(newAliasCmd())
107107
cmd.AddCommand(newServeCmd())
108+
cmd.AddCommand(newLoginCmd())
109+
cmd.AddCommand(newLogoutCmd())
108110

109111
// Define groups
110112
cmd.AddGroup(&cobra.Group{ID: "core", Title: "Core Commands:"})

0 commit comments

Comments
 (0)