package cmd import ( "axolotl/service" "encoding/json" "fmt" "net/http" "os" "time" "github.com/spf13/cobra" ) var loginCmd = &cobra.Command{ Use: "login", Short: "Authenticate with the remote server via OIDC", Run: func(cmd *cobra.Command, args []string) { rc, ok := cfg.GetRemoteConfig() if !ok { fmt.Fprintln(os.Stderr, "no remote server configured; set remote.host in your config") os.Exit(1) } base := fmt.Sprintf("http://%s:%d", rc.Host, rc.Port) resp, err := http.Post(base+"/auth/start", "application/json", nil) if err != nil { fmt.Fprintf(os.Stderr, "failed to contact server: %v\n", err) os.Exit(1) } var start struct { URL string `json:"url"` SessionID string `json:"session_id"` } json.NewDecoder(resp.Body).Decode(&start) resp.Body.Close() if start.URL == "" { fmt.Fprintln(os.Stderr, "server did not return an auth URL; is OIDC configured on the server?") os.Exit(1) } fmt.Printf("Open this URL in your browser:\n\n %s\n\nWaiting for login...\n", start.URL) deadline := time.Now().Add(5 * time.Minute) for time.Now().Before(deadline) { time.Sleep(2 * time.Second) resp, err := http.Get(fmt.Sprintf("%s/auth/poll?session_id=%s", base, start.SessionID)) if err != nil { continue } if resp.StatusCode == http.StatusAccepted { resp.Body.Close() continue } if resp.StatusCode != http.StatusOK { resp.Body.Close() fmt.Fprintln(os.Stderr, "login failed") os.Exit(1) } var result struct { Token string `json:"token"` Username string `json:"username"` } json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() if err := service.SaveSession(&service.Session{Token: result.Token}); err != nil { fmt.Fprintf(os.Stderr, "failed to save session: %v\n", err) os.Exit(1) } fmt.Printf("Logged in as %s\n", result.Username) return } fmt.Fprintln(os.Stderr, "login timed out") os.Exit(1) }, } func init() { rootCmd.AddCommand(loginCmd) }