···44 "context"
55 "errors"
66 "fmt"
77+ "net"
88+ "net/http"
99+ "net/url"
710 "strings"
811912 "github.com/bluesky-social/indigo/atproto/crypto"
···3740// Indicates that resolution process completed successfully, but the DID does not exist.
3841var ErrDIDNotFound = errors.New("DID not found")
39424343+var ErrKeyNotFound = errors.New("identity has no public repo signing key")
4444+4045var DefaultPLCURL = "https://plc.directory"
41464247// Returns a reasonable default Directory implementation for most use cases
4348func DefaultDirectory() Directory {
4444- naive := NewBaseDirectory(DefaultPLCURL)
4545- cached := NewCacheDirectory(&naive)
4949+ base := BaseDirectory{
5050+ PLCURL: DefaultPLCURL,
5151+ HTTPClient: http.DefaultClient,
5252+ Resolver: net.DefaultResolver,
5353+ }
5454+ cached := NewCacheDirectory(&base)
4655 return &cached
4756}
4857···5160 DID syntax.DID
52615362 // Handle/DID mapping must be bi-directionally verified. If that fails, the Handle should be the special 'handle.invalid' value
5454- // TODO: should we make this nullable, instead of 'handle.invalid'?
5563 Handle syntax.Handle
56645757- // These fields represent a parsed subset of a DID document. They are all nullable.
5858- // TODO: should we just embed DIDDocument here?
6565+ // These fields represent a parsed subset of a DID document. They are all nullable. Note that the services and keys maps do not preserve order, so they don't exactly round-trip DID documents.
5966 AlsoKnownAs []string
6060- // TODO: this doesn't preserve order (doesn't round-trip)
6161- Services map[string]Service
6262- // TODO: this doesn't preserve order (doesn't round-trip)
6363- Keys map[string]Key
6767+ Services map[string]Service
6868+ Keys map[string]Key
64696565- // If a valid atproto repo signing public key was parsed, it can be cached here. This is nullable/optional. Calling code should call PublicKey() instead of accessing this member.
7070+ // If a valid atproto repo signing public key was parsed, it can be cached here. This is a nullable/optional field (crypto.PublicKey is an interface). Calling code should use [Identity.PublicKey] instead of accessing this member.
6671 ParsedPublicKey crypto.PublicKey
6772}
6873···7681 URL string
7782}
78838484+// Extracts the information relevant to atproto from an arbitrary DID document.
8585+//
8686+// Always returns an invalid Handle field; calling code should only populate that field if it has been bi-directionally verified.
7987func ParseIdentity(doc *DIDDocument) Identity {
8088 keys := make(map[string]Key, len(doc.VerificationMethod))
8189 for _, vm := range doc.VerificationMethod {
···122130 }
123131}
124132125125-// Identifiers the atproto repo signing public key, specifically, out of any keys associated with this identity.
133133+// Identifies and parses the atproto repo signing public key, specifically, out of any keys associated with this identity.
126134//
127127-// Returns an error if there is no key. Note that 'crypto.PublicKey' is an interface, not a concrete type.
135135+// Returns [ErrKeyNotFound] if there is no such key.
136136+//
137137+// Note that [crypto.PublicKey] is an interface, not a concrete type.
128138func (i *Identity) PublicKey() (crypto.PublicKey, error) {
129139 if i.ParsedPublicKey != nil {
130140 return i.ParsedPublicKey, nil
···134144 }
135145 k, ok := i.Keys["atproto"]
136146 if !ok {
137137- return nil, fmt.Errorf("identity has no atproto public key attached")
147147+ return nil, ErrKeyNotFound
138148 }
139149 switch k.Type {
140150 case "Multikey":
···162172 }
163173}
164174165165-// The home PDS endpoint for this account, if one is included in identity metadata (returns empty string if not found). The endpoint will be an HTTP URL with method, hostname, and optional port, but no path segments.
175175+// The home PDS endpoint for this account, if one is included in identity metadata (returns empty string if not found).
176176+//
177177+// The endpoint should be an HTTP URL with method, hostname, and optional port, and (usually) no path segments.
166178func (i *Identity) PDSEndpoint() string {
167179 if i.Services == nil {
168180 return ""
169181 }
170170- atp, ok := i.Services["atproto_pds"]
182182+ endpoint, ok := i.Services["atproto_pds"]
171183 if !ok {
172184 return ""
173185 }
174174- return atp.URL
186186+ _, err := url.Parse(endpoint.URL)
187187+ if err != nil {
188188+ return ""
189189+ }
190190+ return endpoint.URL
175191}
176192177193// Returns an atproto handle from the alsoKnownAs URI list for this identifier. Returns an error if there is no handle, or if an at:// URI failes to parse as a handle.