Laravel AT Protocol Client (alpha & unstable)
at main 2.5 kB view raw
1<?php 2 3namespace SocialDept\AtpClient\Http; 4 5use Illuminate\Http\Client\PendingRequest; 6use Illuminate\Support\Facades\Http; 7use Psr\Http\Message\RequestInterface; 8use Psr\Http\Message\ResponseInterface; 9use SocialDept\AtpClient\Auth\DPoPKeyManager; 10use SocialDept\AtpClient\Auth\DPoPNonceManager; 11use SocialDept\AtpClient\Data\DPoPKey; 12 13class DPoPClient 14{ 15 public function __construct( 16 protected DPoPKeyManager $dpopManager, 17 protected DPoPNonceManager $nonceManager, 18 ) {} 19 20 /** 21 * Build a DPoP-authenticated request with automatic nonce retry 22 */ 23 public function request( 24 string $pdsEndpoint, 25 string $url, 26 string $method, 27 DPoPKey $dpopKey, 28 ?string $accessToken = null, 29 ): PendingRequest { 30 return Http::retry(times: 2, sleepMilliseconds: 0, throw: false) 31 ->withRequestMiddleware( 32 fn (RequestInterface $request) => $this->addDPoPProof( 33 $request, 34 $pdsEndpoint, 35 $url, 36 $method, 37 $dpopKey, 38 $accessToken, 39 ) 40 ) 41 ->withResponseMiddleware( 42 fn (ResponseInterface $response) => $this->captureNonce($response, $pdsEndpoint) 43 ); 44 } 45 46 /** 47 * Add DPoP proof header to request 48 */ 49 protected function addDPoPProof( 50 RequestInterface $request, 51 string $pdsEndpoint, 52 string $url, 53 string $method, 54 DPoPKey $dpopKey, 55 ?string $accessToken, 56 ): RequestInterface { 57 $nonce = $this->nonceManager->getNonce($pdsEndpoint); 58 59 $dpopProof = $this->dpopManager->createProof( 60 key: $dpopKey, 61 method: $method, 62 url: $url, 63 nonce: $nonce, 64 accessToken: $accessToken, 65 ); 66 67 $request = $request->withHeader('DPoP', $dpopProof); 68 69 if ($accessToken) { 70 $request = $request->withHeader('Authorization', 'DPoP '.$accessToken); 71 } 72 73 return $request; 74 } 75 76 /** 77 * Capture DPoP nonce from response for future requests 78 */ 79 protected function captureNonce(ResponseInterface $response, string $pdsEndpoint): ResponseInterface 80 { 81 $nonce = $response->getHeaderLine('DPoP-Nonce'); 82 83 if ($nonce !== '') { 84 $this->nonceManager->storeNonce($pdsEndpoint, $nonce); 85 } 86 87 return $response; 88 } 89}