I'm developing a website with Laravel 5.7 that has a registration form. When the form is submitted, its params are used to create another user via API.
These API have a .cloudfunctions.net endpoint and the web application is developed using Angular + Firebase.
When I make a GuzzleHttp request to that endpoint, I receive back an HTML response: the google account login page.
The strange thing is that when I run the corresponding cURL command from my vagrant console, or I run the same request with Postman, it returns me a correct json response.
This is a cURL example command:
curl -d '{DATA}' -H "Content-Type: application/json" -u test:test -X POST https://{endpoint}.cloudfunctions.net/api/{function}
And this is my ApiManager class in Laravel project:
namespace App\Utils;
use GuzzleHttp\Client;
/**
* API Manager class.
*/
class ApiManager
{
protected $client;
protected $params;
protected $body;
public function __construct()
{
$this->setupClient();
}
/**
* Setup GuzzleHttp client.
* #return void
*/
private function setupClient()
{
$this->client = new Client([
'base_uri' => env('REMOTE_ENDPOINT'),
'auth' => [env('REMOTE_USERNAME'), env('REMOTE_PASSWORD')],
'headers' => [
'Accept' => 'application/json'
],
'strict' => true
]);
}
/**
* Setup request body as json.
* http://docs.guzzlephp.org/en/stable/request-options.html#json
*
* #param array $params
* #return void
*/
protected function setupBody($params)
{
$this->body = [
'debug' => env('GUZZLE_DEBUG', false),
'json' => $params
];
}
/**
* Decode raw json body to associative array
*
* #param mixed $response
* #return void
*/
protected function getResponseBody($response)
{
$body = json_decode($response->getBody(), true);
if ($body != null && array_key_exists('error', $body)) {
return $body['error'];
}
return $body;
}
/**
* Create user request.
*
* #param array $params
* #return mixed $response
*/
public function createUser($params)
{
$this->setupBody($params);
$response = $this->client->request('POST', '/create-user', $this->body);
if ($response->getStatusCode() == 200) {
return $this->getResponseBody($response);
}
return false;
}
/**
* Update user request.
*
* #param array $params
* #return mixed $response
*/
public function updateUser($params)
{
$this->setupBody($params);
$response = $this->client->request('POST', '/update-user', $this->body);
if ($response->getStatusCode() == 200) {
return $this->getResponseBody($response);
}
return false;
}
}
Anyone can help me find why Guzzle returns the Google account login page, while Postman or cURL from command line work?
Thanks in advance
Related
I'm consuming a API that uses the CloudFront service,
And I'm getting a 403 error for the requests with Guzzle, but if uses for example PHP Curl or call via Postman or Browser works.
Here a log of the Guzzle:
Log of guzzle
And here part of code:
/**
* #return void
*/
public function __construct()
{
$this->client = new Client([
'base_uri' => env('API_HOST'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'timeout' => 30,
'debug' => true,
]);
}
/**
* #param string $method
* #param string $url
* #param array $body
* #param bool $isMultipart
*
* #return ResponseInterface
*/
private function request(string $method, string $url, array $body = [], bool $isMultipart = false): ResponseInterface
{
if ($isMultipart) {
$params['multipart'] = [$body];
} else {
$params['json'] = $body;
}
$url = $this->appedAuthTokensToUrl($url);
return $this->client->request($method, $url, $params);
}
Finally found solution. If had a body in the GET request the Cloudfront will return 403.
So in my case the problem is this if (always setting a body):
if ($isMultipart) {
$params['multipart'] = [$body];
} else {
$params['json'] = $body;
}
Here link of Cloudfront docs
I'm trying to create my first Magento 2 extension and integration and I've been following the guide in their docs here. All good so far, I've completed the auth handshake, stored all the required keys for api requests and can make requests back to my extension fine.
Looking at the OauthClient.php script provided at the foot of the tutorial, the url is hardcoded like so:
return new Uri('http://magento.host/oauth/token/request'); and the tutorial advises you to "Change the instances of http://magento.host in this example to a valid base URL."
<?php
use OAuth\Common\Consumer\Credentials;
use OAuth\Common\Http\Client\ClientInterface;
use OAuth\Common\Http\Exception\TokenResponseException;
use OAuth\Common\Http\Uri\Uri;
use OAuth\Common\Http\Uri\UriInterface;
use OAuth\Common\Storage\TokenStorageInterface;
use OAuth\OAuth1\Service\AbstractService;
use OAuth\OAuth1\Signature\SignatureInterface;
use OAuth\OAuth1\Token\StdOAuth1Token;
use OAuth\OAuth1\Token\TokenInterface;
class OauthClient extends AbstractService
{
/** #var string|null */
protected $_oauthVerifier = null;
public function __construct(
Credentials $credentials,
ClientInterface $httpClient = null,
TokenStorageInterface $storage = null,
SignatureInterface $signature = null,
UriInterface $baseApiUri = null
) {
if (!isset($httpClient)) {
$httpClient = new \OAuth\Common\Http\Client\StreamClient();
}
if (!isset($storage)) {
$storage = new \OAuth\Common\Storage\Session();
}
if (!isset($signature)) {
$signature = new \OAuth\OAuth1\Signature\Signature($credentials);
}
parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
}
/**
* #return UriInterface
*/
public function getRequestTokenEndpoint()
{
return new Uri('http://magento.host/oauth/token/request');
}
/**
* Returns the authorization API endpoint.
*
* #throws \OAuth\Common\Exception\Exception
*/
public function getAuthorizationEndpoint()
{
throw new \OAuth\Common\Exception\Exception(
'Magento REST API is 2-legged. Current operation is not available.'
);
}
/**
* Returns the access token API endpoint.
*
* #return UriInterface
*/
public function getAccessTokenEndpoint()
{
return new Uri('http://magento.host/oauth/token/access');
}
/**
* Parses the access token response and returns a TokenInterface.
*
* #param string $responseBody
* #return TokenInterface
*/
protected function parseAccessTokenResponse($responseBody)
{
return $this->_parseToken($responseBody);
}
/**
* Parses the request token response and returns a TokenInterface.
*
* #param string $responseBody
* #return TokenInterface
* #throws TokenResponseException
*/
protected function parseRequestTokenResponse($responseBody)
{
$data = $this->_parseResponseBody($responseBody);
if (isset($data['oauth_verifier'])) {
$this->_oauthVerifier = $data['oauth_verifier'];
}
return $this->_parseToken($responseBody);
}
/**
* Parse response body and create oAuth token object based on parameters provided.
*
* #param string $responseBody
* #return StdOAuth1Token
* #throws TokenResponseException
*/
protected function _parseToken($responseBody)
{
$data = $this->_parseResponseBody($responseBody);
$token = new StdOAuth1Token();
$token->setRequestToken($data['oauth_token']);
$token->setRequestTokenSecret($data['oauth_token_secret']);
$token->setAccessToken($data['oauth_token']);
$token->setAccessTokenSecret($data['oauth_token_secret']);
$token->setEndOfLife(StdOAuth1Token::EOL_NEVER_EXPIRES);
unset($data['oauth_token'], $data['oauth_token_secret']);
$token->setExtraParams($data);
return $token;
}
/**
* Parse response body and return data in array.
*
* #param string $responseBody
* #return array
* #throws \OAuth\Common\Http\Exception\TokenResponseException
*/
protected function _parseResponseBody($responseBody)
{
if (!is_string($responseBody)) {
throw new TokenResponseException("Response body is expected to be a string.");
}
parse_str($responseBody, $data);
if (null === $data || !is_array($data)) {
throw new TokenResponseException('Unable to parse response.');
} elseif (isset($data['error'])) {
throw new TokenResponseException("Error occurred: '{$data['error']}'");
}
return $data;
}
/**
* #override to fix since parent implementation from lib not sending the oauth_verifier when requesting access token
* Builds the authorization header for an authenticated API request
*
* #param string $method
* #param UriInterface $uri the uri the request is headed
* #param \OAuth\OAuth1\Token\TokenInterface $token
* #param $bodyParams array
* #return string
*/
protected function buildAuthorizationHeaderForAPIRequest(
$method,
UriInterface $uri,
TokenInterface $token,
$bodyParams = null
) {
$this->signature->setTokenSecret($token->getAccessTokenSecret());
$parameters = $this->getBasicAuthorizationHeaderInfo();
if (isset($parameters['oauth_callback'])) {
unset($parameters['oauth_callback']);
}
$parameters = array_merge($parameters, ['oauth_token' => $token->getAccessToken()]);
$parameters = array_merge($parameters, $bodyParams);
$parameters['oauth_signature'] = $this->signature->getSignature($uri, $parameters, $method);
$authorizationHeader = 'OAuth ';
$delimiter = '';
foreach ($parameters as $key => $value) {
$authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
$delimiter = ', ';
}
return $authorizationHeader;
}
}
My Question is how do I pass the url from the store that the integration has been set up on back as a variable in here?(I have it stored in my db)
Thanks for taking the time to take a look.
For anyone who might come across this in future after getting the saved data from the table in my db, I added the Url into the call to make the new class on the checklogin.php script in the documentation:
$oAuthClient = new OauthClient($credentials,$magentoBaseUrl);
And then in OauthClient.php, I added the url to the construct and updated the getRequestTokenEndpoint method and the getAcessTokenEndpoint:
public function __construct(
Credentials $credentials,
$magentoBaseUrl = null,
ClientInterface $httpClient = null,
TokenStorageInterface $storage = null,
SignatureInterface $signature = null,
UriInterface $baseApiUri = null
) {
if (!isset($httpClient)) {
$httpClient = new \OAuth\Common\Http\Client\CurlClient();
}
if (!isset($storage)) {
$storage = new \OAuth\Common\Storage\Session();
}
if (!isset($signature)) {
$signature = new \OAuth\OAuth1\Signature\Signature($credentials);
}
if (!isset($magentoBaseUrl)) {
die();
}
$this->magentoBaseUrl = $magentoBaseUrl;
parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
}
...
/**
* #return UriInterface
*/
public function getRequestTokenEndpoint()
{
return new Uri($this->magentoBaseUrl.'/oauth/token/request');
}
...
/**
* Returns the access token API endpoint.
*
* #return UriInterface
*/
public function getAccessTokenEndpoint()
{
return new Uri($this->magentoBaseUrl.'/oauth/token/access');
}
I have created a wrapper for the Slim request in my application to have the ability to create some custom methods on the Request object.
class Request extends SlimRequest
{
/**
* Get authorization token.
*
* #return null|string
*/
public function getAuthToken(): ?string
{
return $this->getHeaderLine('FOOBAR-TOKEN');
}
/**
* Retrieves a route parameter.
*
* #param string $name
* #return string|null
*/
public function getRouteParam(string $name): ?string
{
return $this->getRoute()->getArgument($name);
}
/**
* Retrieves the route instance.
*
* #return Route
*/
public function getRoute(): Route
{
return $this->getAttribute('route');
}
}
My problem comes when trying to create unit test for this class. The way I have been testing requests is by using Slims build in environment mocks. The first function I added a header to the request which can be seen below, but I can't figure out how to add a Route object to the request
$request = Request::createFromEnvironment(Environment::mock());
$request = $request->withHeader('FOOBAR-TOKEN', 'superSafeExampleToken');
I tried creating the request with a request options but $this->getAttribute('route'); returns null
$requestOptions = [
'REQUEST_METHOD' => 'POST,
'REQUEST_URI' => '/foo/bar',
'QUERY_STRING' => http_build_query($requestParameters),
];
$environment = Environment::mock($requestOptions);
Okay so the solution was the following
public function testGetRouteParam()
{
$route = $route = new Route('GET', '/foo/{bar}', []);
$route->setArguments(['bar' => 1]);
$request = Request::createFromEnvironment(Environment::mock());
$request = $request->withAttribute('route', $route);
$this->assertEquals(1, $request->getRouteParam('bar'));
$this->assertNull($request->getRouteParam('baz'));
}
I'm using Laravel 5.7 along with Passport to create an API for a first-party client. I have a login form that accepts the user's email and password and sends both to a custom LoginController. The LoginController then creates an oAuth payload, sends a POST request to oauth/token via Guzzle and returns the access_token, refresh_token and everything else to my first-party client.
Everything works perfectly when I test it in the browser. However I would now like to write an integration test for all of this and am running into an issue. The issue being that the oAuth server keeps rejecting my client and/or Guzzle request, only during testing.
Here is my corresponding code:
LoginController
<?php
namespace App\Http\Controllers\Api;
use App\Domain\Auth\PasswordGrant;
use App\Http\Requests\LoginRequest;
class LoginController extends ApiController
{
/**
* LoginController constructor.
*/
public function __construct()
{
$this->middleware('api')->only('login');
}
/**
* Attempt to authenticate the user with the credentials they provided
* and if successful, return an access token for the user.
*
* #param LoginRequest $request
* #return \Illuminate\Http\Response
*/
public function login(LoginRequest $request)
{
return PasswordGrant::attempt($request->email, $request->password);
}
}
PasswordGrant
<?php
namespace App\Domain\Auth;
use GuzzleHttp\Client as GuzzleHttp;
use GuzzleHttp\Exception\ClientException;
use Laravel\Passport\Client;
class PasswordGrant
{
/**
* The GuzzleHttp client instance.
*
* #var GuzzleHttp
*/
protected $http;
/**
* PasswordGrant constructor.
*
* #param GuzzleHttp $http
*/
public function __construct(GuzzleHttp $http)
{
$this->http = $http;
}
/**
* #param $username
* #param $password
* #return \Illuminate\Http\Response
*/
public static function attempt($username, $password)
{
$passwordGrant = resolve(static::class);
$payload = $passwordGrant->oAuthPayload(
$passwordGrant->oAuthClient(), $username, $password
);
return $passwordGrant->oAuthResponse($payload);
}
/**
* Get the oAuth Client we are using to authenticate our login and user.
*
* #return Client
*/
protected function oAuthClient()
{
return Client::query()
->where('name', config('api.password_client'))
->where('password_client', true)
->where('revoked', false)
->firstOrFail();
}
/**
* The payload we need to send to our oAuth server in order to receive
* a bearer token and authenticate the user.
*
* #param Client $client
* #param $username
* #param $password
* #return array
*/
protected function oAuthPayload(Client $client, $username, $password)
{
return [
'form_params' => [
'grant_type' => 'password',
'client_id' => $client->id,
'client_secret' => $client->secret,
'username' => $username,
'password' => $password,
'scope' => '*'
]
];
}
/**
* Get the response from our oAuth server.
*
* #param array $payload
* #return \Illuminate\Http\Response
*/
protected function oAuthResponse(array $payload)
{
try {
return $this->http->post(route('passport.token'), $payload)->getBody();
} catch (ClientException $exception) {
return response($exception->getMessage(), $exception->getCode());
}
}
}
PasswordGrantTest
<?php
namespace Tests\Feature\Requests\Team;
use App\Domain\Auth\PasswordGrant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Tests\TestCases\TestCase;
class PasswordGrantTest extends TestCase
{
use RefreshDatabase;
/** #test */
public function it_returns_an_access_token_for_a_user_with_valid_credentials()
{
Artisan::call('passport:client', [
'--password' => true,
'--name' => config('api.password_client')
]);
$user = create(User::class);
$result = PasswordGrant::attempt($user->email, 'secret');
dd($result);
}
}
The dd at the end of my test always returns a 401 with the message:
{"error":"invalid_client","message":"Client authentication failed"}
I have triple checked the existence and validity of my user model, the passport client and made sure the payload is well-formed.
Why does the password grant work when I test it via the browser but it does not work when making the same request to the server from my tests?
Perhaps I am missing certain headers in my request to the server during testing?
I'm working with Symfony 2.8 (PHP) and I'd like to get all versions for every project on Jira via API Rest Jira, and then to filter theme in order to select only the released versions.
I've found this methods but I dont know how to use the 'expand' parameter for reaching versions
Image 1
Image 2
1st step: at config.yml
For more information about Guzzle configuration: http://docs.guzzlephp.org/en/latest/quickstart.html
csa_guzzle:
clients:
jira:
config:
base_uri: "https://jira.*****.*****.***/rest/api/2/"
timeout: 20.0
headers:
Accept: "application/json"
Content-Type: "application/json"
verify: false
auth: ['api','password','Basic']
2nd step: create a GuzzleHttp client service for sending a request to the api
<?php
namespace AppBundle\Service\Atlassian\Jira\Client;
use GuzzleHttp\Client as GuzzleClientHttp;
use GuzzleHttp\Exception\ServerException;
use Psr\Http\Message\ResponseInterface;
class GuzzleClient
{
/**
* Guzzle Client.
*
* #var GuzzleClientHttp
*/
protected $guzzle;
/**
* Response object of request.
*
* #var ResponseInterface
*/
protected $response;
/**
* GuzzleClient constructor.
*
* #param GuzzleClientHttp $guzzle
*/
public function __construct(GuzzleClientHttp $guzzle)
{
$this->guzzle = $guzzle ?: new GuzzleClientHttp();
}
public function send($method = 'GET', $url, $parameters = [])
{
try {
$this->response = $this->guzzle->request($method, $url, ['query' => $parameters]);
} catch (ServerException $exception) {
$this->response = $exception->getResponse();
}
return $this->getContents();
}
/**
* Return the contents as a string of last request.
*
* #return string
*/
public function getContents()
{
return $this->response->getBody()->getContents();
}
/**
* Getter for GuzzleClient.
*
* #return GuzzleClientHttp
*/
public function getClient()
{
return $this->guzzle;
}
/**
* Getter for last Response.
*
* #return ResponseInterface
*/
public function getResponse()
{
return $this->response;
}
3th step: crate a service for getting all versions
<?php
namespace AppBundle\Service\Atlassian\Jira;
use AppBundle\Service\Atlassian\Jira\Client\GuzzleClient;
class ApiService
{
/**
* Client HTTP.
*
* #var GuzzleClient
*/
protected $client;
/**
* ApiService constructor.
*
* #param GuzzleClient $client
*/
public function __construct(GuzzleClient $client)
{
$this->client = $client;
}
/**
* Get all released versions for a given projectKey.
*
* #param string $projectKey
* #return null|array
*/
public function getVersions($projectKey)
{
$versions = json_decode($this->client->send('GET', 'project/'.$projectKey."/versions/"));
for($i=0;$i< count($versions); $i++)
{
if($versions[$i]->released== false)
{
$result = $versions[$i]->name;
}
}
return $versions;
}
}