Resolve AT Protocol DIDs, handles, and schemas with intelligent caching for Laravel
1<?php
2
3namespace SocialDept\AtpResolver\Resolvers;
4
5use GuzzleHttp\Client;
6use GuzzleHttp\Exception\GuzzleException;
7use SocialDept\AtpResolver\Contracts\DidResolver;
8use SocialDept\AtpResolver\Data\DidDocument;
9use SocialDept\AtpResolver\Exceptions\DidResolutionException;
10use SocialDept\AtpResolver\Support\Concerns\HasConfig;
11use SocialDept\AtpResolver\Support\Concerns\ParsesDid;
12
13class WebDidResolver implements DidResolver
14{
15 use HasConfig;
16 use ParsesDid;
17
18 protected Client $client;
19
20 /**
21 * Create a new Web DID resolver instance.
22 */
23 public function __construct(?int $timeout = null)
24 {
25 $this->client = new Client([
26 'timeout' => $timeout ?? $this->getConfig('resolver.timeout', 10),
27 'headers' => [
28 'Accept' => 'application/json',
29 'User-Agent' => 'Beacon/1.0',
30 ],
31 ]);
32 }
33
34 /**
35 * Resolve a DID:Web to a DID Document.
36 *
37 * @param string $did The DID to resolve (e.g., "did:web:example.com")
38 */
39 public function resolve(string $did): DidDocument
40 {
41 if (! $this->supports($this->extractMethod($did))) {
42 throw DidResolutionException::unsupportedMethod($this->extractMethod($did));
43 }
44
45 $url = $this->buildDidUrl($did);
46
47 try {
48 $response = $this->client->get($url);
49 $data = json_decode($response->getBody()->getContents(), true);
50
51 if (! is_array($data)) {
52 throw DidResolutionException::resolutionFailed($did, 'Invalid response format');
53 }
54
55 return DidDocument::fromArray($data);
56 } catch (GuzzleException $e) {
57 throw DidResolutionException::resolutionFailed($did, $e->getMessage());
58 }
59 }
60
61 /**
62 * Check if this resolver supports the given DID method.
63 *
64 * @param string $method The DID method (e.g., "web")
65 */
66 public function supports(string $method): bool
67 {
68 return $method === 'web';
69 }
70
71 /**
72 * Build the URL to fetch the DID document from.
73 *
74 * @param string $did
75 */
76 protected function buildDidUrl(string $did): string
77 {
78 $identifier = $this->extractIdentifier($did);
79
80 // Decode URL-encoded characters
81 $domain = str_replace('%3A', ':', $identifier);
82
83 // Split domain and path
84 $parts = explode(':', $domain);
85 $domainName = array_shift($parts);
86
87 // If there's a path, append it; otherwise use .well-known/did.json
88 if (count($parts) > 0) {
89 $path = implode('/', $parts).'/did.json';
90 } else {
91 $path = '.well-known/did.json';
92 }
93
94 return "https://{$domainName}/{$path}";
95 }
96}