+61
-8
atproto/client/cmd/atclient/main.go
+61
-8
atproto/client/cmd/atclient/main.go
···
8
8
"os"
9
9
10
10
"github.com/bluesky-social/indigo/atproto/client"
11
+
"github.com/bluesky-social/indigo/atproto/identity"
12
+
"github.com/bluesky-social/indigo/atproto/syntax"
11
13
12
14
"github.com/urfave/cli/v2"
13
15
)
···
22
24
Usage: "do a basic GET request",
23
25
Action: runGet,
24
26
},
27
+
&cli.Command{
28
+
Name: "login-refresh",
29
+
Usage: "do a basic login and GET request",
30
+
Action: runLoginRefresh,
31
+
Flags: []cli.Flag{
32
+
&cli.StringFlag{
33
+
Name: "username",
34
+
Required: true,
35
+
Aliases: []string{"u"},
36
+
Usage: "handle or DID (not email)",
37
+
},
38
+
&cli.StringFlag{
39
+
Name: "password",
40
+
Required: true,
41
+
Aliases: []string{"p"},
42
+
Usage: "password (or app password)",
43
+
},
44
+
},
45
+
},
25
46
},
26
47
}
27
48
h := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})
···
29
50
app.RunAndExitOnError()
30
51
}
31
52
53
+
func simpleGet(ctx context.Context, c *client.APIClient) error {
54
+
params := map[string]string{
55
+
"actor": "atproto.com",
56
+
"limit": "5",
57
+
"includePins": "false",
58
+
}
59
+
60
+
var d json.RawMessage
61
+
err := c.Get(ctx, "app.bsky.feed.getAuthorFeed", params, &d)
62
+
if err != nil {
63
+
return err
64
+
}
65
+
66
+
out, err := json.MarshalIndent(d, "", " ")
67
+
if err != nil {
68
+
return err
69
+
}
70
+
fmt.Println(string(out))
71
+
return nil
72
+
}
73
+
32
74
func runGet(cctx *cli.Context) error {
33
75
ctx := context.Background()
34
76
···
36
78
Host: "https://public.api.bsky.app",
37
79
}
38
80
39
-
params := map[string]string{
40
-
"actor": "atproto.com",
41
-
"limit": "5",
42
-
"includePins": "false",
81
+
return simpleGet(ctx, &c)
82
+
}
83
+
84
+
func runLoginRefresh(cctx *cli.Context) error {
85
+
ctx := context.Background()
86
+
87
+
atid, err := syntax.ParseAtIdentifier(cctx.String("username"))
88
+
if err != nil {
89
+
return err
43
90
}
44
-
b, err := c.Get(ctx, "app.bsky.feed.getAuthorFeed", params)
91
+
92
+
dir := identity.DefaultDirectory()
93
+
ident, err := dir.Lookup(ctx, *atid)
45
94
if err != nil {
46
95
return err
47
96
}
48
97
49
-
out, err := json.MarshalIndent(b, "", " ")
98
+
c := client.APIClient{
99
+
Host: ident.PDSEndpoint(),
100
+
}
101
+
102
+
_, err = client.NewSession(ctx, &c, atid.String(), cctx.String("password"), "")
50
103
if err != nil {
51
104
return err
52
105
}
53
-
fmt.Println(string(out))
54
-
return nil
106
+
107
+
return simpleGet(ctx, &c)
55
108
}
+39
atproto/client/refresh_auth.go
+39
atproto/client/refresh_auth.go
···
1
1
package client
2
2
3
3
import (
4
+
"context"
5
+
"fmt"
4
6
"net/http"
7
+
"sync"
8
+
9
+
comatproto "github.com/bluesky-social/indigo/api/atproto"
5
10
6
11
"github.com/bluesky-social/indigo/atproto/syntax"
7
12
)
···
12
17
DID syntax.DID
13
18
// The AuthHost might different from any APIClient host, if there is an entryway involved
14
19
AuthHost string
20
+
21
+
lk sync.Mutex
15
22
}
16
23
17
24
// TODO:
···
28
35
func (a *RefreshAuth) AccountDID() syntax.DID {
29
36
return a.DID
30
37
}
38
+
39
+
// updates the client with the new auth method
40
+
func NewSession(ctx context.Context, client *APIClient, username, password, token string) (*RefreshAuth, error) {
41
+
42
+
reqBody := comatproto.ServerCreateSession_Input{
43
+
Identifier: username,
44
+
Password: password,
45
+
}
46
+
if token != "" {
47
+
reqBody.AuthFactorToken = &token
48
+
}
49
+
50
+
var out comatproto.ServerCreateSession_Output
51
+
err := client.Post(ctx, syntax.NSID("com.atproto.server.createSession"), &reqBody, &out)
52
+
if err != nil {
53
+
return nil, err
54
+
}
55
+
56
+
if out.Active != nil && *out.Active == false {
57
+
return nil, fmt.Errorf("account is disabled: %s", out.Status)
58
+
}
59
+
60
+
ra := RefreshAuth{
61
+
AccessToken: out.AccessJwt,
62
+
RefreshToken: out.RefreshJwt,
63
+
DID: syntax.DID(out.Did),
64
+
// TODO: authHost / PDS host distinction
65
+
AuthHost: client.Host,
66
+
}
67
+
client.Auth = &ra
68
+
return &ra, nil
69
+
}