this repo has no description
1package identity 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "strings" 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "github.com/haileyok/cocoon/plc" 14) 15 16func ResolveHandle(ctx context.Context, handle string) (string, error) { 17 var did string 18 19 _, err := syntax.ParseHandle(handle) 20 if err != nil { 21 return "", err 22 } 23 24 recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 25 if err == nil { 26 for _, rec := range recs { 27 if strings.HasPrefix(rec, "did=") { 28 did = strings.Split(rec, "did=")[1] 29 break 30 } 31 } 32 } else { 33 fmt.Printf("erorr getting txt records: %v\n", err) 34 } 35 36 if did == "" { 37 req, err := http.NewRequestWithContext( 38 ctx, 39 "GET", 40 fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 41 nil, 42 ) 43 if err != nil { 44 return "", nil 45 } 46 47 resp, err := http.DefaultClient.Do(req) 48 if err != nil { 49 return "", nil 50 } 51 defer resp.Body.Close() 52 53 if resp.StatusCode != http.StatusOK { 54 io.Copy(io.Discard, resp.Body) 55 return "", fmt.Errorf("unable to resolve handle") 56 } 57 58 b, err := io.ReadAll(resp.Body) 59 if err != nil { 60 return "", err 61 } 62 63 maybeDid := string(b) 64 65 if _, err := syntax.ParseDID(maybeDid); err != nil { 66 return "", fmt.Errorf("unable to resolve handle") 67 } 68 69 did = maybeDid 70 } 71 72 return did, nil 73} 74 75type DidDoc struct { 76 Context []string `json:"@context"` 77 Id string `json:"id"` 78 AlsoKnownAs []string `json:"alsoKnownAs"` 79 VerificationMethods []DidDocVerificationMethod `json:"verificationMethods"` 80 Service []DidDocService `json:"service"` 81} 82 83type DidDocVerificationMethod struct { 84 Id string `json:"id"` 85 Type string `json:"type"` 86 Controller string `json:"controller"` 87 PublicKeyMultibase string `json:"publicKeyMultibase"` 88} 89 90type DidDocService struct { 91 Id string `json:"id"` 92 Type string `json:"type"` 93 ServiceEndpoint string `json:"serviceEndpoint"` 94} 95 96type DidData struct { 97 Did string `json:"did"` 98 VerificationMethods map[string]string `json:"verificationMethods"` 99 RotationKeys []string `json:"rotationKeys"` 100 AlsoKnownAs []string `json:"alsoKnownAs"` 101 Services map[string]DidDataService `json:"services"` 102} 103 104type DidDataService struct { 105 Type string `json:"type"` 106 Endpoint string `json:"endpoint"` 107} 108 109type DidLog []DidLogEntry 110 111type DidLogEntry struct { 112 Sig string `json:"sig"` 113 Prev *string `json:"prev"` 114 Type string `json:"string"` 115 Services map[string]plc.OperationService `json:"services"` 116 AlsoKnownAs []string `json:"alsoKnownAs"` 117 RotationKeys []string `json:"rotationKeys"` 118 VerificationMethods map[string]string `json:"verificationMethods"` 119} 120 121type DidAuditEntry struct { 122 Did string `json:"did"` 123 Operation DidLogEntry `json:"operation"` 124 Cid string `json:"cid"` 125 Nullified bool `json:"nullified"` 126 CreatedAt string `json:"createdAt"` 127} 128 129type DidAuditLog []DidAuditEntry 130 131func FetchDidDoc(ctx context.Context, did string) (*DidDoc, error) { 132 var ustr string 133 if strings.HasPrefix(did, "did:plc:") { 134 ustr = fmt.Sprintf("https://plc.directory/%s", did) 135 } else if strings.HasPrefix(did, "did:web:") { 136 ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 137 } else { 138 return nil, fmt.Errorf("did was not a supported did type") 139 } 140 141 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 142 if err != nil { 143 return nil, err 144 } 145 146 resp, err := http.DefaultClient.Do(req) 147 if err != nil { 148 return nil, err 149 } 150 defer resp.Body.Close() 151 152 if resp.StatusCode != 200 { 153 io.Copy(io.Discard, resp.Body) 154 return nil, fmt.Errorf("could not find identity in plc registry") 155 } 156 157 var diddoc DidDoc 158 if err := json.NewDecoder(resp.Body).Decode(&diddoc); err != nil { 159 return nil, err 160 } 161 162 return &diddoc, nil 163} 164 165func FetchDidData(ctx context.Context, did string) (*DidData, error) { 166 var ustr string 167 ustr = fmt.Sprintf("https://plc.directory/%s/data", did) 168 169 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 170 if err != nil { 171 return nil, err 172 } 173 174 resp, err := http.DefaultClient.Do(req) 175 if err != nil { 176 return nil, err 177 } 178 defer resp.Body.Close() 179 180 if resp.StatusCode != 200 { 181 io.Copy(io.Discard, resp.Body) 182 return nil, fmt.Errorf("could not find identity in plc registry") 183 } 184 185 var diddata DidData 186 if err := json.NewDecoder(resp.Body).Decode(&diddata); err != nil { 187 return nil, err 188 } 189 190 return &diddata, nil 191} 192 193func FetchDidAuditLog(ctx context.Context, did string) (DidAuditLog, error) { 194 var ustr string 195 ustr = fmt.Sprintf("https://plc.directory/%s/log/audit", did) 196 197 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 198 if err != nil { 199 return nil, err 200 } 201 202 resp, err := http.DefaultClient.Do(req) 203 if err != nil { 204 return nil, err 205 } 206 defer resp.Body.Close() 207 208 if resp.StatusCode != 200 { 209 io.Copy(io.Discard, resp.Body) 210 return nil, fmt.Errorf("could not find identity in plc registry") 211 } 212 213 var didlog DidAuditLog 214 if err := json.NewDecoder(resp.Body).Decode(&didlog); err != nil { 215 return nil, err 216 } 217 218 return didlog, nil 219} 220 221func ResolveService(ctx context.Context, did string) (string, error) { 222 diddoc, err := FetchDidDoc(ctx, did) 223 if err != nil { 224 return "", err 225 } 226 227 var service string 228 for _, svc := range diddoc.Service { 229 if svc.Id == "#atproto_pds" { 230 service = svc.ServiceEndpoint 231 } 232 } 233 234 if service == "" { 235 return "", fmt.Errorf("could not find atproto_pds service in identity services") 236 } 237 238 return service, nil 239}