Skip to content

Commit baa8bf2

Browse files
committed
feat: add Codex OAuth session transfer from local to remote
1 parent fab8968 commit baa8bf2

1 file changed

Lines changed: 60 additions & 3 deletions

File tree

pkg/cmd/open/open.go

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,8 +1099,12 @@ func openCodex(t *terminal.Terminal, sshAlias string, path string, codexArgs []s
10991099
return breverrors.WrapAndTrace(err)
11001100
}
11011101

1102-
// Auto-authenticate: only forward a key if the remote is not already logged in
1102+
// Auto-authenticate: try API key first, then OAuth token transfer
11031103
apiKey := resolveCodexAPIKey(t, sshAlias)
1104+
if apiKey == "" {
1105+
// No API key found; try transferring OAuth session from local auth.json
1106+
tryTransferCodexOAuthSession(t, sshAlias)
1107+
}
11041108

11051109
sessionName := "codex"
11061110

@@ -1152,15 +1156,68 @@ func resolveCodexAPIKey(t *terminal.Terminal, sshAlias string) string {
11521156
}
11531157

11541158
// isRemoteCodexAuthenticated checks whether the remote already has
1155-
// OPENAI_API_KEY set in the shell.
1159+
// OPENAI_API_KEY set in the shell or an OAuth session in ~/.codex/auth.json.
11561160
func isRemoteCodexAuthenticated(sshAlias string) bool {
11571161
checkCmd := exec.Command(
11581162
"ssh", sshAlias,
1159-
`printenv OPENAI_API_KEY >/dev/null 2>&1`,
1163+
`printenv OPENAI_API_KEY >/dev/null 2>&1 || test -f "$HOME/.codex/auth.json"`,
11601164
) // #nosec G204
11611165
return checkCmd.Run() == nil
11621166
}
11631167

1168+
// tryTransferCodexOAuthSession checks for a local ~/.codex/auth.json and
1169+
// offers to transfer it to the remote instance. This is a session transfer:
1170+
// the local file is removed after copying so the token is only active on
1171+
// the remote machine.
1172+
func tryTransferCodexOAuthSession(t *terminal.Terminal, sshAlias string) {
1173+
homeDir, err := os.UserHomeDir()
1174+
if err != nil {
1175+
return
1176+
}
1177+
localAuthPath := homeDir + "/.codex/auth.json"
1178+
1179+
// Check if local auth.json exists
1180+
if _, err := os.Stat(localAuthPath); os.IsNotExist(err) {
1181+
return
1182+
}
1183+
1184+
t.Vprintf("%s", t.Yellow("\nFound Codex OAuth session in ~/.codex/auth.json\n"))
1185+
t.Vprintf("%s", t.Yellow("Transferring this session will move your auth to the remote instance\n"))
1186+
t.Vprintf("%s", t.Yellow("and log you out locally (the token can only be active in one place).\n\n"))
1187+
1188+
result := terminal.PromptSelectInput(terminal.PromptSelectContent{
1189+
Label: "Transfer your Codex OAuth session to the remote instance?",
1190+
Items: []string{"Yes, transfer and log out locally", "No, skip"},
1191+
})
1192+
1193+
if result != "Yes, transfer and log out locally" {
1194+
return
1195+
}
1196+
1197+
// Ensure remote ~/.codex directory exists
1198+
mkdirCmd := exec.Command("ssh", sshAlias, `mkdir -p "$HOME/.codex"`) // #nosec G204
1199+
if err := mkdirCmd.Run(); err != nil {
1200+
t.Vprintf(t.Red("Failed to create remote ~/.codex directory: %v\n"), err)
1201+
return
1202+
}
1203+
1204+
// SCP the auth.json to remote
1205+
scpCmd := exec.Command("scp", localAuthPath, sshAlias+":~/.codex/auth.json") // #nosec G204
1206+
output, err := scpCmd.CombinedOutput()
1207+
if err != nil {
1208+
t.Vprintf(t.Red("Failed to transfer auth.json: %s\n%s\n"), err, string(output))
1209+
return
1210+
}
1211+
1212+
// Remove local auth.json
1213+
if err := os.Remove(localAuthPath); err != nil {
1214+
t.Vprintf(t.Red("Transferred to remote but failed to remove local auth.json: %v\n"), err)
1215+
return
1216+
}
1217+
1218+
t.Vprintf("%s", t.Green("OAuth session transferred to remote instance. You are now logged out locally.\n"))
1219+
}
1220+
11641221
func ensureCodexInstalled(t *terminal.Terminal, sshAlias string) error {
11651222
checkCmd := fmt.Sprintf(
11661223
"ssh %s 'export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"; export PATH=\"$HOME/.local/bin:$HOME/.npm-global/bin:$PATH\"; which codex >/dev/null 2>&1'",

0 commit comments

Comments
 (0)