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\HandleResolver;
8use SocialDept\AtpResolver\Exceptions\HandleResolutionException;
9use SocialDept\AtpResolver\Support\Concerns\HasConfig;
10
11class AtProtoHandleResolver implements HandleResolver
12{
13 use HasConfig;
14
15 protected Client $client;
16
17 protected string $pdsEndpoint;
18
19 /**
20 * Create a new AT Protocol handle resolver instance.
21 *
22 * @param string|null $pdsEndpoint The PDS endpoint to use for resolution
23 */
24 public function __construct(?string $pdsEndpoint = null, ?int $timeout = null)
25 {
26 $this->pdsEndpoint = $pdsEndpoint ?? $this->getConfig('resolver.pds_endpoint', 'https://bsky.social');
27 $this->client = new Client([
28 'timeout' => $timeout ?? $this->getConfig('resolver.timeout', 10),
29 'headers' => [
30 'Accept' => 'application/json',
31 'User-Agent' => 'Beacon/1.0',
32 ],
33 ]);
34 }
35
36 /**
37 * Resolve a handle to a DID.
38 *
39 * @param string $handle The handle to resolve (e.g., "user.bsky.social")
40 * @return string The resolved DID
41 */
42 public function resolve(string $handle): string
43 {
44 $this->validateHandle($handle);
45
46 try {
47 $response = $this->client->get("{$this->pdsEndpoint}/xrpc/com.atproto.identity.resolveHandle", [
48 'query' => ['handle' => $handle],
49 ]);
50
51 $data = json_decode($response->getBody()->getContents(), true);
52
53 if (! isset($data['did'])) {
54 throw HandleResolutionException::resolutionFailed($handle, 'No DID in response');
55 }
56
57 return $data['did'];
58 } catch (GuzzleException $e) {
59 throw HandleResolutionException::resolutionFailed($handle, $e->getMessage());
60 }
61 }
62
63 /**
64 * Validate a handle format.
65 *
66 * @param string $handle
67 */
68 protected function validateHandle(string $handle): void
69 {
70 // Handle must be a valid domain name
71 if (! preg_match('/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $handle)) {
72 throw HandleResolutionException::invalidFormat($handle);
73 }
74 }
75}