A CLI for tangled.sh
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}