this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

atproto/identity: early work on identity SDK

+181
+52
atproto/identity/did.go
··· 1 + package identity 2 + 3 + type DIDDocument struct { 4 + DID syntax.DID `json:"id"` 5 + AlsoKnownAs []string `json:"alsoKnownAs,omitempty"` 6 + VerificationMethod []DocVerificationMethod `json:"alsoKnownAs,omitempty"` 7 + Service []DocService `json:"alsoKnownAs,omitempty"` 8 + } 9 + 10 + type DocVerificationMethod struct { 11 + ID `json:"id"` 12 + Type `json:"type"` 13 + Controller `json:"controller"` 14 + PublicKeyMultibase `json:"publicKeyMultibase"` 15 + } 16 + 17 + type DocService struct { 18 + ID `json:"id"` 19 + Type `json:"type"` 20 + ServiceEndpoint `json:"serviceEndpoint"` 21 + } 22 + 23 + var ErrDIDNotFound = error.New("DID not found") 24 + 25 + // WARNING: this does *not* bi-directionally verify account metadata; it only implements direct DID-to-DID-document lookup for the supported DID methods, and parses the resulting DID Doc into an Account struct 26 + func ResolveDID(ctx context.Context, did identifier.DID) (*DIDDocument, error) { 27 + switch did.Method() { 28 + case "web": 29 + panic("NOT IMPLEMENTED") 30 + case "plc": 31 + panic("NOT IMPLEMENTED") 32 + default: 33 + return nil, fmt.Errorf("DID method not supported: %s", did.Method()) 34 + } 35 + } 36 + 37 + func ResolveDIDWeb(ctx context.Context, did identifier.DID) (*DIDDocument, error) { 38 + // XXX 39 + } 40 + 41 + func ResolvePLC(ctx context.Context, did identifier.DID) (*DIDDocument, error) { 42 + // XXX 43 + } 44 + 45 + func (d *DIDDocument) Account() Account { 46 + // XXX 47 + } 48 + 49 + // "Renders" a DID Document 50 + func (a *Account) DIDDocument() DIDDocument { 51 + // XXX 52 + }
+5
atproto/identity/doc.go
··· 1 + /* Package identity provides types and routines for resolving handles and DIDs from the network 2 + 3 + Much of the code in this SDK is based on existing code in indigo:api/extra.go 4 + */ 5 + package identity
+59
atproto/identity/handle.go
··· 1 + package identity 2 + 3 + import ( 4 + "net" 5 + "context" 6 + ) 7 + 8 + var ErrHandleNotFound = error.New("handle not found") 9 + 10 + // Does not cross-verify, just does the handle resolution step. 11 + func ResolveHandleDNS(ctx context.Context, handle identifier.Handle) (identifier.DID, error) { 12 + 13 + res, err := net.LookupTXT("_atproto." + handle) 14 + if err != nil { 15 + return "", fmt.Errorf("handle DNS resolution failed: %w", err) 16 + } 17 + 18 + for _, s := range res { 19 + if strings.HasPrefix(s, "did=") { 20 + parts := strings.SplitN(s, "=", 2) 21 + did, err := syntax.ParseDID(parts[1]) 22 + if err != nil { 23 + return "", fmt.Errorf("invalid DID in handle DNS record: %w", err) 24 + } 25 + return did, nil 26 + } 27 + } 28 + return "", ErrHandleNotFound 29 + } 30 + 31 + func ResolveHandleWellKnown(ctx context.Context, handle identifier.Handle) (identifier.DID, error) { 32 + // NOTE: could pull a client or transport from context 33 + c := http.DefaultClient 34 + 35 + req, err := http.NewRequest("GET", fmt.Sprintf("https://%s/.well-known/atproto-did", handle), nil) 36 + if err != nil { 37 + return "", err 38 + } 39 + req = req.WithContext(ctx) 40 + 41 + resp, err := c.Do(req) 42 + if err != nil { 43 + return "", fmt.Errorf("failed to resolve handle (%s) through HTTP well-known route: %s", handle, err) 44 + } 45 + if resp.StatusCode != 200 { 46 + return "", fmt.Errorf("failed to resolve handle (%s) through HTTP well-known route: status=%d", handle, resp.StatusCode) 47 + } 48 + 49 + if resp.ContentLength > 2048 { 50 + return "", fmt.Errorf("HTTP well-known route returned too much data during handle resolution") 51 + } 52 + 53 + b, err := io.ReadAll(io.LimitReader(resp.Body, 2048)) 54 + if err != nil { 55 + return "", fmt.Errorf("HTTP well-known response fail to read: %w", err) 56 + } 57 + line := strings.TrimSpace(string(b)) 58 + return syntax.ParseDID(line) 59 + }
+65
atproto/identity/identity.go
··· 1 + package identity 2 + 3 + import ( 4 + "fmt" 5 + 6 + "github.com/bluesky-social/indigo/atproto/syntax" 7 + ) 8 + 9 + // API for doing account lookups by DID or handle, with bi-directional verification handled automatically. 10 + // 11 + // Handles which fail to resolve or don't match DID alsoKnownAs are an error. DIDs which resolve but the handle does not resolve back to the DID return an Account where the Handle is the special `handle.invalid` value. 12 + // 13 + // Some example implementations of this interface would be: 14 + // - naive direct resolution on every call 15 + // - API client, which just makes requests to PDS (or other remote service) 16 + // - simple in-memory caching wrapper layer to reduce network hits 17 + // - services with backing datastore to do sophisticated caching, TTL, auto-refresh, etc 18 + type AccountCatalog interface { 19 + LookupHandle(h syntax.Handle) (*Account, error) 20 + LookupDID(d syntax.DID) (*Account, error) 21 + } 22 + 23 + // Represents an atproto identity. Could be a regular user account, or a service account (eg, feed generator) 24 + type Account struct { 25 + // these fields are required and non-nullable 26 + DID syntax.DID 27 + Handle syntax.Handle 28 + 29 + // these fields are nullable 30 + AlsoKnownAs []string 31 + Services map[string]Service 32 + Keys map[string]Key 33 + } 34 + 35 + type Key struct { 36 + Type string 37 + PublicKeyMultibase string 38 + } 39 + 40 + type Service struct { 41 + Type string 42 + URL string 43 + } 44 + 45 + func (a *Account) SigningKey() *Key { 46 + if a.Keys == nil { 47 + return nil 48 + } 49 + atp := a.Keys["atproto"] 50 + if atp == nil { 51 + return nil 52 + } 53 + return &atp 54 + } 55 + 56 + func (a *Account) PDS() string { 57 + if a.Services == nil { 58 + return nil 59 + } 60 + atp := a.Services["atproto_pds"] 61 + if atp == nil { 62 + return nil 63 + } 64 + return &atp 65 + }