an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1namespace PDSharp.Core
2
3open System
4open System.Net.Http
5open System.Text.Json
6open System.Text.Json.Serialization
7
8module DidResolver =
9 type VerificationMethod = {
10 [<JsonPropertyName("id")>]
11 Id : string
12 [<JsonPropertyName("type")>]
13 Type : string
14 [<JsonPropertyName("controller")>]
15 Controller : string
16 [<JsonPropertyName("publicKeyMultibase")>]
17 PublicKeyMultibase : string option
18 }
19
20 type DidDocument = {
21 [<JsonPropertyName("id")>]
22 Id : string
23 [<JsonPropertyName("verificationMethod")>]
24 VerificationMethod : VerificationMethod list
25 }
26
27 let private httpClient = new HttpClient()
28
29 let private fetchJson<'T> (url : string) : Async<'T option> = async {
30 try
31 let! response = httpClient.GetAsync url |> Async.AwaitTask
32
33 if response.IsSuccessStatusCode then
34 let! stream = response.Content.ReadAsStreamAsync() |> Async.AwaitTask
35 let options = JsonSerializerOptions(PropertyNameCaseInsensitive = true)
36 let! doc = JsonSerializer.DeserializeAsync<'T>(stream, options).AsTask() |> Async.AwaitTask
37 return Some doc
38 else
39 return None
40 with _ ->
41 return None
42 }
43
44 let resolveDidWeb (did : string) : Async<DidDocument option> = async {
45 let parts = did.Split(':')
46
47 if parts.Length < 3 then
48 return None
49 else
50 let domain = parts.[2]
51
52 let url =
53 if domain = "localhost" then
54 "http://localhost:5000/.well-known/did.json"
55 else
56 $"https://{domain}/.well-known/did.json"
57
58 return! fetchJson<DidDocument> url
59 }
60
61 let resolveDidPlc (did : string) : Async<DidDocument option> = async {
62 let url = $"https://plc.directory/{did}"
63 return! fetchJson<DidDocument> url
64 }
65
66 let resolve (did : string) : Async<DidDocument option> = async {
67 if did.StartsWith("did:web:") then
68 return! resolveDidWeb did
69 elif did.StartsWith("did:plc:") then
70 return! resolveDidPlc did
71 else
72 return None
73 }