+73
-54
identity/identity.go
+73
-54
identity/identity.go
···
13
"github.com/bluesky-social/indigo/util"
14
)
15
16
-
func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) {
17
-
if cli == nil {
18
-
cli = util.RobustHTTPClient()
19
-
}
20
-
21
-
var did string
22
-
23
-
_, err := syntax.ParseHandle(handle)
24
if err != nil {
25
-
return "", err
26
}
27
28
-
recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle))
29
-
if err == nil {
30
-
for _, rec := range recs {
31
-
if strings.HasPrefix(rec, "did=") {
32
-
did = strings.Split(rec, "did=")[1]
33
-
break
34
}
35
}
36
-
} else {
37
-
fmt.Printf("erorr getting txt records: %v\n", err)
38
}
39
40
-
if did == "" {
41
-
req, err := http.NewRequestWithContext(
42
-
ctx,
43
-
"GET",
44
-
fmt.Sprintf("https://%s/.well-known/atproto-did", handle),
45
-
nil,
46
-
)
47
-
if err != nil {
48
-
return "", nil
49
-
}
50
51
-
resp, err := http.DefaultClient.Do(req)
52
-
if err != nil {
53
-
return "", nil
54
-
}
55
-
defer resp.Body.Close()
56
57
-
if resp.StatusCode != http.StatusOK {
58
-
io.Copy(io.Discard, resp.Body)
59
-
return "", fmt.Errorf("unable to resolve handle")
60
-
}
61
62
-
b, err := io.ReadAll(resp.Body)
63
-
if err != nil {
64
-
return "", err
65
-
}
66
67
-
maybeDid := string(b)
68
69
-
if _, err := syntax.ParseDID(maybeDid); err != nil {
70
-
return "", fmt.Errorf("unable to resolve handle")
71
-
}
72
73
-
did = maybeDid
74
}
75
76
-
return did, nil
77
}
78
79
-
func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) {
80
if cli == nil {
81
cli = util.RobustHTTPClient()
82
}
83
84
-
var ustr string
85
if strings.HasPrefix(did, "did:plc:") {
86
-
ustr = fmt.Sprintf("https://plc.directory/%s", did)
87
} else if strings.HasPrefix(did, "did:web:") {
88
-
ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:"))
89
} else {
90
-
return nil, fmt.Errorf("did was not a supported did type")
91
}
92
93
req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil)
···
95
return nil, err
96
}
97
98
-
resp, err := http.DefaultClient.Do(req)
99
if err != nil {
100
return nil, err
101
}
···
103
104
if resp.StatusCode != 200 {
105
io.Copy(io.Discard, resp.Body)
106
-
return nil, fmt.Errorf("could not find identity in plc registry")
107
}
108
109
var diddoc DidDoc
···
127
return nil, err
128
}
129
130
-
resp, err := http.DefaultClient.Do(req)
131
if err != nil {
132
return nil, err
133
}
···
13
"github.com/bluesky-social/indigo/util"
14
)
15
16
+
func ResolveHandleFromTXT(ctx context.Context, handle string) (string, error) {
17
+
name := fmt.Sprintf("_atproto.%s", handle)
18
+
recs, err := net.LookupTXT(name)
19
if err != nil {
20
+
return "", fmt.Errorf("handle could not be resolved via txt: %w", err)
21
}
22
23
+
for _, rec := range recs {
24
+
if strings.HasPrefix(rec, "did=") {
25
+
maybeDid := strings.Split(rec, "did=")[1]
26
+
if _, err := syntax.ParseDID(maybeDid); err == nil {
27
+
return maybeDid, nil
28
}
29
}
30
}
31
32
+
return "", fmt.Errorf("handle could not be resolved via txt: no record found")
33
+
}
34
35
+
func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) {
36
+
ustr := fmt.Sprintf("https://%s/.well=known/atproto-did", handle)
37
+
req, err := http.NewRequestWithContext(
38
+
ctx,
39
+
"GET",
40
+
ustr,
41
+
nil,
42
+
)
43
+
if err != nil {
44
+
return "", fmt.Errorf("handle could not be resolved via web: %w", err)
45
+
}
46
47
+
resp, err := cli.Do(req)
48
+
if err != nil {
49
+
return "", fmt.Errorf("handle could not be resolved via web: %w", err)
50
+
}
51
+
defer resp.Body.Close()
52
53
+
b, err := io.ReadAll(resp.Body)
54
+
if err != nil {
55
+
return "", fmt.Errorf("handle could not be resolved via web: %w", err)
56
+
}
57
58
+
if resp.StatusCode != http.StatusOK {
59
+
return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode)
60
+
}
61
62
+
maybeDid := string(b)
63
64
+
if _, err := syntax.ParseDID(maybeDid); err != nil {
65
+
return "", fmt.Errorf("handle could not be resolved via web: invalid did in document")
66
}
67
68
+
return maybeDid, nil
69
}
70
71
+
func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) {
72
if cli == nil {
73
cli = util.RobustHTTPClient()
74
}
75
76
+
_, err := syntax.ParseHandle(handle)
77
+
if err != nil {
78
+
return "", err
79
+
}
80
+
81
+
if maybeDidFromTxt, err := ResolveHandleFromTXT(ctx, handle); err == nil {
82
+
return maybeDidFromTxt, nil
83
+
}
84
+
85
+
if maybeDidFromWeb, err := ResolveHandleFromWellKnown(ctx, cli, handle); err == nil {
86
+
return maybeDidFromWeb, nil
87
+
}
88
+
89
+
return "", fmt.Errorf("handle could not be resolved")
90
+
}
91
+
92
+
func DidToDocUrl(did string) (string, error) {
93
if strings.HasPrefix(did, "did:plc:") {
94
+
return fmt.Sprintf("https://plc.directory/%s", did), nil
95
} else if strings.HasPrefix(did, "did:web:") {
96
+
return fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")), nil
97
} else {
98
+
return "", fmt.Errorf("did was not a supported did type")
99
+
}
100
+
}
101
+
102
+
func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) {
103
+
if cli == nil {
104
+
cli = util.RobustHTTPClient()
105
+
}
106
+
107
+
ustr, err := DidToDocUrl(did)
108
+
if err != nil {
109
+
return nil, err
110
}
111
112
req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil)
···
114
return nil, err
115
}
116
117
+
resp, err := cli.Do(req)
118
if err != nil {
119
return nil, err
120
}
···
122
123
if resp.StatusCode != 200 {
124
io.Copy(io.Discard, resp.Body)
125
+
return nil, fmt.Errorf("unable to find did doc at url. did: %s. url: %s", did, ustr)
126
}
127
128
var diddoc DidDoc
···
146
return nil, err
147
}
148
149
+
resp, err := cli.Do(req)
150
if err != nil {
151
return nil, err
152
}
+16
-5
identity/passport.go
+16
-5
identity/passport.go
···
19
type Passport struct {
20
h *http.Client
21
bc BackingCache
22
-
lk sync.Mutex
23
}
24
25
func NewPassport(h *http.Client, bc BackingCache) *Passport {
···
30
return &Passport{
31
h: h,
32
bc: bc,
33
-
lk: sync.Mutex{},
34
}
35
}
36
···
38
skipCache, _ := ctx.Value("skip-cache").(bool)
39
40
if !skipCache {
41
cached, ok := p.bc.GetDoc(did)
42
if ok {
43
return cached, nil
44
}
45
}
46
47
-
p.lk.Lock() // this is pretty pathetic, and i should rethink this. but for now, fuck it
48
-
defer p.lk.Unlock()
49
-
50
doc, err := FetchDidDoc(ctx, p.h, did)
51
if err != nil {
52
return nil, err
53
}
54
55
p.bc.PutDoc(did, doc)
56
57
return doc, nil
58
}
···
61
skipCache, _ := ctx.Value("skip-cache").(bool)
62
63
if !skipCache {
64
cached, ok := p.bc.GetDid(handle)
65
if ok {
66
return cached, nil
67
}
···
72
return "", err
73
}
74
75
p.bc.PutDid(handle, did)
76
77
return did, nil
78
}
79
80
func (p *Passport) BustDoc(ctx context.Context, did string) error {
81
return p.bc.BustDoc(did)
82
}
83
84
func (p *Passport) BustDid(ctx context.Context, handle string) error {
85
return p.bc.BustDid(handle)
86
}
···
19
type Passport struct {
20
h *http.Client
21
bc BackingCache
22
+
mu sync.RWMutex
23
}
24
25
func NewPassport(h *http.Client, bc BackingCache) *Passport {
···
30
return &Passport{
31
h: h,
32
bc: bc,
33
}
34
}
35
···
37
skipCache, _ := ctx.Value("skip-cache").(bool)
38
39
if !skipCache {
40
+
p.mu.RLock()
41
cached, ok := p.bc.GetDoc(did)
42
+
p.mu.RUnlock()
43
+
44
if ok {
45
return cached, nil
46
}
47
}
48
49
+
// TODO: should coalesce requests here
50
doc, err := FetchDidDoc(ctx, p.h, did)
51
if err != nil {
52
return nil, err
53
}
54
55
+
p.mu.Lock()
56
p.bc.PutDoc(did, doc)
57
+
p.mu.Unlock()
58
59
return doc, nil
60
}
···
63
skipCache, _ := ctx.Value("skip-cache").(bool)
64
65
if !skipCache {
66
+
p.mu.RLock()
67
cached, ok := p.bc.GetDid(handle)
68
+
p.mu.RUnlock()
69
+
70
if ok {
71
return cached, nil
72
}
···
77
return "", err
78
}
79
80
+
p.mu.Lock()
81
p.bc.PutDid(handle, did)
82
+
p.mu.Unlock()
83
84
return did, nil
85
}
86
87
func (p *Passport) BustDoc(ctx context.Context, did string) error {
88
+
p.mu.Lock()
89
+
defer p.mu.Unlock()
90
return p.bc.BustDoc(did)
91
}
92
93
func (p *Passport) BustDid(ctx context.Context, handle string) error {
94
+
p.mu.Lock()
95
+
defer p.mu.Unlock()
96
return p.bc.BustDid(handle)
97
}