77use Cake \Chronos \Chronos ;
88use Ecodev \Felix \Validator \IPRange ;
99use Exception ;
10+ use Laminas \Diactoros \CallbackStream ;
1011use Psr \Http \Message \ResponseInterface ;
1112use Psr \Http \Message \ServerRequestInterface ;
1213use Psr \Http \Server \MiddlewareInterface ;
@@ -44,18 +45,18 @@ public function __construct(
4445 public function process (ServerRequestInterface $ request , RequestHandlerInterface $ handler ): ResponseInterface
4546 {
4647 if ($ this ->required ) {
47- $ this ->verify ($ request );
48+ $ request = $ this ->verify ($ request );
4849 }
4950
5051 return $ handler ->handle ($ request );
5152 }
5253
53- private function verify (ServerRequestInterface $ request ): void
54+ private function verify (ServerRequestInterface $ request ): ServerRequestInterface
5455 {
5556 $ signature = $ request ->getHeader ('X-Signature ' )[0 ] ?? '' ;
5657 if (!$ signature ) {
5758 if ($ this ->isAllowedIp ($ request )) {
58- return ;
59+ return $ request ;
5960 }
6061
6162 throw new Exception ('Missing `X-Signature` HTTP header in signed query ' , 403 );
@@ -66,10 +67,11 @@ private function verify(ServerRequestInterface $request): void
6667 $ hash = $ m ['hash ' ];
6768
6869 $ this ->verifyTimestamp ($ timestamp );
69- $ this ->verifyHash ($ request , $ timestamp , $ hash );
70- } else {
71- throw new Exception ('Invalid `X-Signature` HTTP header in signed query ' , 403 );
70+
71+ return $ this ->verifyHash ($ request , $ timestamp , $ hash );
7272 }
73+
74+ throw new Exception ('Invalid `X-Signature` HTTP header in signed query ' , 403 );
7375 }
7476
7577 private function verifyTimestamp (string $ timestamp ): void
@@ -83,33 +85,45 @@ private function verifyTimestamp(string $timestamp): void
8385 }
8486 }
8587
86- private function verifyHash (ServerRequestInterface $ request , string $ timestamp , string $ hash ): void
88+ private function verifyHash (ServerRequestInterface $ request , string $ timestamp , string $ hash ): ServerRequestInterface
8789 {
88- $ operations = $ this ->getOperations ($ request );
90+ [ ' request ' => $ request , ' operations ' => $ operations ] = $ this ->getOperations ($ request );
8991 $ payload = $ timestamp . $ operations ;
9092
9193 foreach ($ this ->keys as $ key ) {
9294 $ computedHash = hash_hmac ('sha256 ' , $ payload , $ key );
9395 if ($ hash === $ computedHash ) {
94- return ;
96+ return $ request ;
9597 }
9698 }
9799
98100 throw new Exception ('Invalid signed query ' , 403 );
99101 }
100102
101- private function getOperations (ServerRequestInterface $ request ): mixed
103+ /**
104+ * @return array{request: ServerRequestInterface, operations: string}
105+ */
106+ private function getOperations (ServerRequestInterface $ request ): array
102107 {
103108 $ contents = $ request ->getBody ()->getContents ();
109+
104110 if ($ contents ) {
105- return $ contents ;
111+ return [
112+ // Pseudo-rewind the request, even if non-rewindable, so the next
113+ // middleware still accesses the stream from the beginning
114+ 'request ' => $ request ->withBody (new CallbackStream (fn () => $ contents )),
115+ 'operations ' => $ contents ,
116+ ];
106117 }
107118
108119 $ parsedBody = $ request ->getParsedBody ();
109120 if (is_array ($ parsedBody )) {
110121 $ operations = $ parsedBody ['operations ' ] ?? null ;
111122 if ($ operations ) {
112- return $ operations ;
123+ return [
124+ 'request ' => $ request ,
125+ 'operations ' => $ operations ,
126+ ];
113127 }
114128 }
115129
0 commit comments