Skip to content

Commit ee71378

Browse files
committed
feat: add Codex OAuth session transfer from local to remote
1 parent 0b7a8f5 commit ee71378

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
@@ -1100,8 +1100,12 @@ func openCodex(t *terminal.Terminal, sshAlias string, path string, codexArgs []s
11001100
return breverrors.WrapAndTrace(err)
11011101
}
11021102

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

11061110
sessionName := "codex"
11071111

@@ -1153,15 +1157,68 @@ func resolveCodexAPIKey(t *terminal.Terminal, sshAlias string) string {
11531157
}
11541158

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

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