A CLI for tangled.sh
at main 2.1 kB view raw
1package auth 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 "sync" 10 11 "github.com/charmbracelet/huh" 12 "github.com/charmbracelet/huh/spinner" 13 "github.com/spf13/cobra" 14 "tangled.sh/rockorager.dev/knit" 15 "tangled.sh/rockorager.dev/knit/config" 16) 17 18func Login(cmd *cobra.Command, args []string) error { 19 var ( 20 handle string 21 appPassword string 22 ) 23 24 host := knit.DefaultHost 25 26 handleInput := huh.NewInput(). 27 Title("Handle"). 28 Value(&handle) 29 30 if err := handleInput.Run(); err != nil { 31 return fmt.Errorf("getting handle: %w", err) 32 } 33 34 appPasswordInput := huh.NewInput(). 35 Title("App Password"). 36 EchoMode(huh.EchoModePassword). 37 Value(&appPassword) 38 39 if err := appPasswordInput.Run(); err != nil { 40 return fmt.Errorf("getting app-password: %w", err) 41 } 42 43 ctx, stop := context.WithCancel(cmd.Context()) 44 defer stop() 45 46 wg := &sync.WaitGroup{} 47 wg.Add(1) 48 49 go func(wg *sync.WaitGroup) { 50 spinner.New(). 51 Context(ctx). 52 Title("Authenticating..."). 53 Run() 54 wg.Done() 55 }(wg) 56 57 cookies, err := login(host, handle, appPassword) 58 if err != nil { 59 return fmt.Errorf("authenticating: %w", err) 60 } 61 62 if err := SaveCookies(cookies, knit.DefaultHost, handle); err != nil { 63 return err 64 } 65 66 if err := config.AddHandleToHost(host, handle); err != nil { 67 return err 68 } 69 70 stop() 71 wg.Wait() 72 fmt.Printf("\x1b[32m✔\x1b[m %s logged in to %s\r\n", handle, host) 73 return nil 74} 75 76func login(host string, handle string, appPassword string) ([]*http.Cookie, error) { 77 form := url.Values{} 78 form.Set("handle", handle) 79 form.Set("app_password", appPassword) 80 81 u := url.URL{ 82 Scheme: "https", 83 Host: host, 84 Path: "/login", 85 } 86 87 resp, err := http.Post( 88 u.String(), 89 "application/x-www-form-urlencoded", 90 strings.NewReader(form.Encode()), 91 ) 92 if err != nil { 93 return nil, err 94 } 95 defer resp.Body.Close() 96 97 if resp.StatusCode != http.StatusOK { 98 return nil, fmt.Errorf("unexpected status code: %d", 99 resp.StatusCode) 100 } 101 102 if len(resp.Cookies()) == 0 { 103 return nil, fmt.Errorf("session cookie not found") 104 } 105 106 return resp.Cookies(), nil 107}