/** * {@inheritDoc} */ public function authenticate(RequestInterface $request) { $authHeader = AuthorizationHeader::createFromRequest($request); $signature = $authHeader->getSignature(); // Check whether the timestamp is valid. $comparison = $this->compareTimestamp($request, $this->expiry); if (-1 == $comparison) { throw new TimestampOutOfRangeException('Request is too old'); } elseif (1 == $comparison) { throw new TimestampOutOfRangeException('Request is too far in the future'); } // Load the API Key and sign the request. if (!($key = $this->keyLoader->load($authHeader->getId()))) { throw new KeyNotFoundException('API key not found'); } // Generate the signature from the passed authorization header. // If it matches the request signature, the request is authenticated. $compareRequest = $request->withoutHeader('Authorization'); $authHeaderBuilder = new AuthorizationHeaderBuilder($compareRequest, $key); $authHeaderBuilder->setRealm($authHeader->getRealm()); $authHeaderBuilder->setId($authHeader->getId()); $authHeaderBuilder->setNonce($authHeader->getNonce()); $authHeaderBuilder->setVersion($authHeader->getVersion()); $authHeaderBuilder->setCustomHeaders($authHeader->getCustomHeaders()); $compareAuthHeader = $authHeaderBuilder->getAuthorizationHeader(); $compareSignature = $compareAuthHeader->getSignature(); if (!hash_equals($compareSignature, $signature)) { throw new InvalidSignatureException('Signature not valid'); } return $key; }
/** * Ensures the HTTP HMAC middleware signs requests correctly. */ public function testAuthorizationHeader() { $realm = 'CIStore'; $headers = ['X-Authorization-Timestamp' => '1432075982', 'X-Custom-Signer1' => 'custom-1', 'X-Custom-Signer2' => 'custom-2']; $authKey = new Key('e7fe97fa-a0c8-4a42-ab8e-2c26d52df059', 'bXlzZWNyZXRzZWNyZXR0aGluZ3Rva2VlcA=='); $request = new Request('GET', 'https://example.pipeline.io/api/v1/ci/pipelines', $headers); $authHeaderBuilder = new AuthorizationHeaderBuilder($request, $authKey); $authHeaderBuilder->setRealm($realm); $authHeaderBuilder->setId('e7fe97fa-a0c8-4a42-ab8e-2c26d52df059'); $authHeaderBuilder->setNonce('a9938d07-d9f0-480c-b007-f1e956bcd027'); $authHeaderBuilder->setCustomHeaders(['X-Custom-Signer2', 'X-Custom-Signer1']); $authHeader = $authHeaderBuilder->getAuthorizationHeader(); $middleware = new MockHmacAuthMiddleware($authKey, $realm, ['X-Custom-Signer1', 'X-Custom-Signer2'], $authHeader); $request = $middleware->signRequest($request); $expected = 'acquia-http-hmac realm="CIStore",' . 'id="e7fe97fa-a0c8-4a42-ab8e-2c26d52df059",' . 'nonce="a9938d07-d9f0-480c-b007-f1e956bcd027",' . 'version="2.0",' . 'headers="X-Custom-Signer1%3BX-Custom-Signer2",' . 'signature="yoHiYvx79ssSDIu3+OldpbFs8RsjrMXgRoM89d5t+zA="'; $this->assertEquals($expected, $request->getHeaderLine('Authorization')); }
/** * @dataProvider specFixtureProvider */ public function testSpec($input, $expectations) { $key = new Key($input['id'], $input['secret']); $digest = new Digest(); $headers = ['X-Authorization-Timestamp' => $input['timestamp'], 'Content-Type' => $input['content_type']]; foreach ($input['headers'] as $header => $value) { $headers[$header] = $value; } $body = !empty($input['content_body']) ? $input['content_body'] : null; $request = new Request($input['method'], $input['url'], $headers, $body); $authHeaderBuilder = new AuthorizationHeaderBuilder($request, $key); $authHeaderBuilder->setRealm($input['realm']); $authHeaderBuilder->setId($input['id']); $authHeaderBuilder->setNonce($input['nonce']); $authHeaderBuilder->setVersion('2.0'); $authHeaderBuilder->setCustomHeaders($input['signed_headers']); $authHeader = $authHeaderBuilder->getAuthorizationHeader(); $requestSigner = new MockRequestSigner($key, $input['realm'], $digest, $authHeader); $signedRequest = $requestSigner->signRequest($request, $input['signed_headers']); $signedAuthHeader = $signedRequest->getHeaderLine('Authorization'); $this->assertContains('id="' . $input['id'] . '"', $signedAuthHeader); $this->assertContains('nonce="' . $input['nonce'] . '"', $signedAuthHeader); $this->assertContains('realm="' . rawurlencode($input['realm']) . '"', $signedAuthHeader); $this->assertContains('signature="' . $expectations['message_signature'] . '"', $signedAuthHeader); $this->assertContains('version="2.0"', $signedAuthHeader); // Prove that the digest generates the correct signature. $signedMessage = $digest->sign($expectations['signable_message'], $input['secret']); $this->assertEquals($expectations['message_signature'], $signedMessage); // Prove that the authenticator can authenticate the request. $keyLoader = new MockKeyLoader([$input['id'] => $input['secret']] + $this->keys); $authenticator = new MockRequestAuthenticator($keyLoader, null, $input['timestamp']); $compareKey = $authenticator->authenticate($signedRequest); $this->assertEquals($compareKey->getId(), $input['id']); // Prove that the response signer generates the correct signature. $response = new Response(200, [], $expectations['response_body']); $responseSigner = new ResponseSigner($key, $signedRequest); $response = $responseSigner->signResponse($response); $this->assertTrue($response->hasHeader('X-Server-Authorization-HMAC-SHA256')); $this->assertEquals($expectations['response_signature'], $response->getHeaderLine('X-Server-Authorization-HMAC-SHA256')); }
/** * Builds an AuthorizationHeader object. * * @param \Psr\Http\Message\RequestInterface $request * The request being signed. * @param string[] $customHeaders * A list of custom header names. The values of the headers will be * extracted from the request. * * @return \Acquia\Hmac\AuthorizationHeader * The compiled authorizatio header object. */ protected function buildAuthorizationHeader(RequestInterface $request, array $customHeaders = []) { $authHeaderBuilder = new AuthorizationHeaderBuilder($request, $this->key, $this->digest); $authHeaderBuilder->setRealm($this->realm); $authHeaderBuilder->setId($this->key->getId()); $authHeaderBuilder->setCustomHeaders($customHeaders); return $authHeaderBuilder->getAuthorizationHeader(); }