| 109 | } |
| 110 | |
| 111 | func ResolveDIDFromHandle(handle string) (*string, error) { |
| 112 | // Validate our handle |
| 113 | if !regexp.MustCompile(`^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`).MatchString(handle) { |
| 114 | return nil, errors.New("invalid handle") |
| 115 | } |
| 116 | userDID := "" |
| 117 | |
| 118 | // Get the handle's DID |
| 119 | |
| 120 | // Get DID thru .well-known, since this is what the most common handle PDS, bsky.social uses. |
| 121 | wellKnownDIDResp, err := http.Get(fmt.Sprintf("https://%s/.well-known/atproto-did", handle)) |
| 122 | if err == nil { |
| 123 | bodyBytes, _ := io.ReadAll(wellKnownDIDResp.Body) |
| 124 | bodyString := string(bodyBytes) |
| 125 | |
| 126 | // Remove newline charectors, since it likes to fail the regex with them |
| 127 | bodyString = strings.ReplaceAll(bodyString, "\n", "") |
| 128 | // Check if the body is a DID |
| 129 | if regexp.MustCompile(`^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`).MatchString(bodyString) { |
| 130 | userDID = bodyString |
| 131 | } |
| 132 | wellKnownDIDResp.Body.Close() |
| 133 | } |
| 134 | if userDID == "" { |
| 135 | // Get DID through _atproto DNS records |
| 136 | txts, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) |
| 137 | if err == nil { |
| 138 | for _, txt := range txts { |
| 139 | txt = strings.ReplaceAll(txt, "\n", "") |
| 140 | if regexp.MustCompile(`^did=did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$`).MatchString(txt) { |
| 141 | userDID = txt[4:] // Extract the DID without the 'did=' prefix |
| 142 | break |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | if userDID == "" { |
| 149 | return nil, errors.New("user does not exist") |
| 150 | } |
| 151 | |
| 152 | return &userDID, nil |
| 153 | } |
| 154 | |
| 155 | func ResolvePDSFromDID(userDID string) (*string, error) { |
| 156 | // we must do different things depending on the DID type. |