Need to send password and username as POST fields
Closed this issue · 7 comments
Hi,
for the API from DHL I need to send the username and password as POST fields (application/x-www-form-urlencoded). The grant_type is still client_credentials. How can I add these two fields in my request? Thanks for your help!
<?php
require_once('vendor/autoload.php');
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use kamermans\OAuth2\GrantType\ClientCredentials;
use kamermans\OAuth2\OAuth2Middleware;
use kamermans\OAuth2\Signer\ClientCredentials\PostFormData;
try {
$reauth_client = new Client(['base_uri' => 'https://api-eu.dhl.com/post/de/shipping/im/v1/user', 'debug' => false]);
$reauth_config = [
"grant_type" => "client_credentials",
"client_id" => "<MY CLIENT ID>",
"client_secret" => "<MY SECRET>",
"username" => "<MY USERNAME>",
"password" => "<MY PASSWORD>"
];
$grant_type = new ClientCredentials($reauth_client, $reauth_config);
$oauth = new OAuth2Middleware($grant_type);
$oauth->setClientCredentialsSigner(new PostFormData());
$stack = HandlerStack::create();
$stack->push($oauth);
// This is the normal Guzzle client that you use in your application
$client = new GuzzleHttp\Client(['handler' => $stack, 'auth' => 'oauth']);
$response = $client->get('https://api-eu.dhl.com/post/de/shipping/im/v1/user/profile');
echo "Status: ".$response->getStatusCode()."\n";
I think you'd have to create a new Signer
based on kamermans\OAuth2\Signer\ClientCredentials\PostFormData
:
<?php
// DHLFormData.php
// Custom form data signer for DHL
use kamermans\OAuth2\Utils\Helper;
use kamermans\OAuth2\Signer\ClientCredentials\SignerInterface;
use GuzzleHttp\Post\PostBodyInterface;
class DHLFormData implements SignerInterface
{
private $username;
private $password;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
public function sign($request, $clientId, $clientSecret)
{
if (Helper::guzzleIs('>=', 6)) {
if ($request->getHeaderLine('Content-Type') != 'application/x-www-form-urlencoded') {
throw new \RuntimeException('Unable to set fields in request body');
}
parse_str($request->getBody(), $data);
$data['client_id'] = $clientId;
$data['client_secret'] = $clientSecret;
$data['username'] = $this->username;
$data['password'] = $this->password;
$body_stream = Helper::streamFor(http_build_query($data, '', '&'));
return $request->withBody($body_stream);
}
$body = $request->getBody();
if (!($body instanceof PostBodyInterface)) {
throw new \RuntimeException('Unable to set fields in request body');
}
$body->setField('client_id', $clientId);
$body->setField('client_secret', $clientSecret);
$body->setField('username', $this->username);
$body->setField('password', $this->password);
return $request;
}
}
And then use it like this:
<?php
require_once('vendor/autoload.php');
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use kamermans\OAuth2\GrantType\ClientCredentials;
use kamermans\OAuth2\OAuth2Middleware;
try {
$reauth_client = new Client(['base_uri' => 'https://api-eu.dhl.com/post/de/shipping/im/v1/user', 'debug' => false]);
$reauth_config = [
"grant_type" => "client_credentials",
"client_id" => "<MY CLIENT ID>",
"client_secret" => "<MY SECRET>"
];
$signer = new DHLFormData("<MY USERNAME>", "<MY PASSWORD>");
$grant_type = new ClientCredentials($reauth_client, $reauth_config);
$oauth = new OAuth2Middleware($grant_type);
$oauth->setClientCredentialsSigner($signer);
$stack = HandlerStack::create();
$stack->push($oauth);
// This is the normal Guzzle client that you use in your application
$client = new GuzzleHttp\Client(['handler' => $stack, 'auth' => 'oauth']);
$response = $client->get('https://api-eu.dhl.com/post/de/shipping/im/v1/user/profile');
echo "Status: ".$response->getStatusCode()."\n";
Thank you very much. I seems that DHL does not respond with "access_token". They call it "userToken". I think this can not handle guzzle-oauth2-subscriber without some changes, right? Can you help me a second time?
{ "userToken": "ABCDEFG...=", "walletBalance": 1000000, "showTermsAndConditions": true, "infoMessage": "", "token_type": "BearerToken", "expires_in": 86400, "issued_at": "Thu, 02 May 2024 20:15:21 GMT", "external_customer_id": "<MY CUSTOMER ID>", "authenticated_user": "<MY USERNAME>" }
Weird, that definitely goes against the OAuth2 spec. Let me see if that's adjustable.
This is similar to GitHub applications, which use username, password and a different field for access token:
https://github.com/kamermans/guzzle-oauth2-subscriber/blob/master/src/GrantType/Specific/GithubApplication.php#L104
Ok, how about this :)
DHLAuth.php
<?php
use GuzzleHttp\ClientInterface;
use kamermans\OAuth2\Utils\Collection;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use kamermans\OAuth2\Utils\Helper;
use kamermans\OAuth2\GrantType\GrantTypeInterface;
use kamermans\OAuth2\Signer\ClientCredentials\SignerInterface;
/**
* DHL Authentication
*/
class DHLAuth implements GrantTypeInterface
{
/**
* The token endpoint client.
*
* @var ClientInterface
*/
private $client;
/**
* Configuration settings.
*
* @var Collection
*/
private $config;
/**
* @param ClientInterface $client
* @param array $config
*/
public function __construct(ClientInterface $client, array $config)
{
$this->client = $client;
$this->config = Collection::fromConfig(
$config,
// Defaults
[],
// Required
[
'client_id',
'client_secret',
'username',
'password',
]
);
}
public function getRawData(SignerInterface $clientCredentialsSigner, $refreshToken = null)
{
if (Helper::guzzleIs('>=', 6)) {
$request = (new \GuzzleHttp\Psr7\Request('POST', ''))
->withBody($this->getPostBody())
->withHeader('Content-Type', 'application/x-www-form-urlencoded');
} else {
$request = $this->client->createRequest('POST', null);
$request->setBody($this->getPostBody());
}
$response = $this->client->send($request);
// Restructure some fields from the GitHub response
/* Example Response:
{
"userToken": "ABCDEFG...=",
"walletBalance": 1000000,
"showTermsAndConditions": true,
"infoMessage": "",
"token_type": "BearerToken",
"expires_in": 86400,
"issued_at": "Thu, 02 May 2024 20:15:21 GMT",
"external_customer_id": "<MY CUSTOMER ID>",
"authenticated_user": "<MY USERNAME>"
}
*/
$data = json_decode($response->getBody(), true);
$data['access_token'] = $data['userToken'];
unset($data['userToken']);
return $data;
}
protected function getPostBody()
{
$postBody = [
'client_id' => $this->config['client_id'],
'client_secret' => $this->config['client_secret'],
'username' => $this->config['username'],
'password' => $this->config['password'],
];
$postBody = http_build_query($postBody, '', '&');
return Helper::guzzleIs('<', 6) ? Stream::factory($postBody) : Helper::streamFor($postBody);
}
}
Example:
<?php
require_once('vendor/autoload.php');
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use kamermans\OAuth2\GrantType\ClientCredentials;
use kamermans\OAuth2\OAuth2Middleware;
use kamermans\OAuth2\Signer\ClientCredentials\PostFormData;
try {
$reauth_client = new Client(['base_uri' => 'https://api-eu.dhl.com/post/de/shipping/im/v1/user', 'debug' => false]);
$reauth_config = [
"grant_type" => "client_credentials",
"client_id" => "<MY CLIENT ID>",
"client_secret" => "<MY SECRET>",
"username" => "<MY USERNAME>",
"password" => "<MY PASSWORD>",
];
$grant_type = new DHLAuth($reauth_client, $reauth_config);
$oauth = new OAuth2Middleware($grant_type);
$stack = HandlerStack::create();
$stack->push($oauth);
// This is the normal Guzzle client that you use in your application
$client = new GuzzleHttp\Client(['handler' => $stack, 'auth' => 'oauth']);
$response = $client->get('https://api-eu.dhl.com/post/de/shipping/im/v1/user/profile');
echo "Status: ".$response->getStatusCode()."\n";
Note that this is completely untested and it would be up to you to maintain it, but I think it does what you want, and if it does work fine, I can add it to this repo as a specific grant type like GithubApplication
.
Thanks you very much. It works if you add
}
catch (Exception $e) {
echo "Caught exception: ".$e->getMessage()."\n";
}
at the end (I forget this too). You have to add "grant_type" as another required field and 'grant_type' => $this->config['client_credentials'], in the $postBody Array.
In DHLAuth.php you have to replace the word "Github" with "DHL". The result should be // Restructure some fields from the DHL response
Thanks for the info and I'm glad it's working for you :)