How to pass through credentials to PHP SOAP Request - php

I need to create a PHP SOAP client based on this specification:
https://exdev.server.propctrl.com/v5.4/Basic/AgencyIntegration.svc?wsdl
I am able to create my soap client, and when I try to call a function it returns saying access denied:
Message: Access is denied.
Based on documentation, there is no verification/auth method that I first need to call, but it seems I have to use a type called Credentials.
struct Credentials { string Password; string Username; }
I then tried created a Credentials object:
class Credentials {
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}
And assigned new values to it and used it in my SoapHeaders:
$credentials = new Credentials($username, $password);
$header = new SoapHeader('http://localhost','Credentials',$credentials,false);
$client->__setSoapHeaders($header);
When I then use a function I still get the same error:
echo "<pre>";
try {
$response = $client->__soapCall("EchoAuthenticated", array("EchoAuthenticated" => "asdfasdf"));
var_dump($response);
}
//catch exception
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
Message: Access is denied.
I guess my question is that I don't know where/how to pass through the credentials in order for the API to authorise my request.
Any ideas/suggestions?
update1:

The authentication method should be explained in some documentation, anyway there are Soap services that use HTTP Header for authentication, for example
$soapClient->setHttpHeaders([
'clientId' => $username,
'clientSecret' => $password
]);
But please consider that keys ( in my example clientId and clientSecret ) depend by the service implementation.
Do you have a service documentation?

Related

Issue with DocuSign sending Envelopes

I recently changed my DocuSign integration to use the JWT OAuth flow. To achieve this I have a few classes.
OAuth Client
<?php
namespace App\DocuSign;
use DocuSign\eSign\Client\ApiClient;
use DocuSign\eSign\Client\Auth\OAuth;
use DocuSign\eSign\Configuration;
use Exception;
use Illuminate\Support\Facades\Log;
/**
* Helper class to generate a DocuSign Client instance using JWT OAuth2.
*
* #see
*
*/
class OAuthClient
{
/**
* Create a new DocuSign API Client instance using JWT based OAuth2.
*/
public static function createApiClient()
{
$config = (new Configuration())->setHost(config('docusign.host'));
$oAuth = (new OAuth())->setOAuthBasePath(config('docusign.oauth_base_path'));
$apiClient = new ApiClient($config, $oAuth);
try {
$response = $apiClient->requestJWTUserToken(
config('docusign.integrator_key'),
config('docusign.user_id'),
config('docusign.private_key'),
'signature impersonation',
60
);
if ($response) {
$accessToken = $response[0]['access_token'];
$config->addDefaultHeader('Authorization', 'Bearer ' . $accessToken);
$apiClient = new ApiClient($config);
return $apiClient;
}
} catch (Exception $e) {
// If consent is required we just need to give the consent URL.
if (strpos($e->getMessage(), 'consent_required') !== false) {
$authorizationUrl = config('docusign.oauth_base_path') . '/oauth/auth?' . http_build_query([
'scope' => 'signature impersonation',
'redirect_uri' => config('docusign.redirect_url'),
'client_id' => config('docusign.integrator_key'),
'response_type' => 'code'
]);
Log::critical('Consent not given for DocuSign API', [
'authorization_url' => $authorizationUrl
]);
abort(500, 'Consent has not been given to use the DocuSign API');
}
throw $e;
}
}
}
Signature Client Service
<?php
namespace App\DocuSign;
use DocuSign\eSign\Api\EnvelopesApi;
use DocuSign\eSign\Client\ApiClient;
class SignatureClientService
{
/**
* DocuSign API Client
*/
public ApiClient $apiClient;
/**
* Create a new instance of our class.
*/
public function __construct()
{
$this->apiClient = OAuthClient::createApiClient();
}
/**
* Getter for the EnvelopesApi
*/
public function getEnvelopeApi(): EnvelopesApi
{
return new EnvelopesApi($this->apiClient);
}
}
Then, in my constructors where I want to use it I'm doing
/**
* Create a new controller instance
*/
public function __construct()
{
$this->clientService = new SignatureClientService();
$this->envelopesApi = $this->clientService->getEnvelopeApi();
}
Finally, I use it like so
$envelopeSummary = $this->envelopesApi->createEnvelope(config('docusign.api_account_id'), $envelopeDefinition);
But I get an error that reads
DocuSign\eSign\Client\ApiException: Error while requesting server,
received a non successful HTTP code [400] with response Body:
O:8:"stdClass":2:{s:9:"errorCode";s:21:"USER_LACKS_MEMBERSHIP";s:7:"message";s:60:"The
UserID does not have a valid membership in this Account.";} in
/homepages/45/d641872465/htdocs/sites/ita-portal/vendor/docusign/esign-client/src/Client/ApiClient.php:344
I researched this and this would imply that the user is not within the account, but they are. I also checked that this account owns the envelopes that I'm trying to send.
For reference I took inspiration for envelope sending from here: https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-template-remote/
What I think is happening is that the request is going to the wrong server or the wrong account.
I'd suggest using a packet analyser like Fiddler or Wireshark to log where your requests are headed (or just log the request within your application)
The auth URLs seem to be correct since you're not getting a 401 unauthorised error but the envelopes and other queries' must match the base URL located in your account under the Apps and Keys page. It would be of the form demo.docusign.net for our demo environment or xxx.docusign.net for our production environment

How do I authenticate a service account in Google Cloud Services in PHP?

While developing Recaptcha Enterprise for use of the V2 "I am not a robot" checkbox, I am stuck on this error:
Fatal error: Uncaught DomainException: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information
I follow the link and have settled on this to authenticate:
use Google\Cloud\Storage\StorageClient;
$storage = new StorageClient([
'keyFile' => json_decode(file_get_contents($path_to_keyfile), true),
'projectId' => 'MY_PROJECT'
]);
I cannot find anything else that suggests I need to do anything more than this, and this link to the constructor API doesn't suggest I can pass it in as a parameter and then proceed. I do not want to use environment variables for this project, I want to connect manually in the code. What am I missing? I can confirm I have a working service account.
If it's helpful, the code I'm trying to run after I presumably authenticate is this:
// ==================== CAPTCHA ===================
use Google\Cloud\RecaptchaEnterprise\V1\RecaptchaEnterpriseServiceClient;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
$captcha_response = $_POST['g-recaptcha-response'];
$site_key = "123456789abc";
$client = new RecaptchaEnterpriseServiceClient();
define('SITE_KEY', $site_key);
define('TOKEN', $captcha_response);
define('PROTECTED_ACTION', 'signup');
define('PARENT_PROJECT', 'projects/MY_PROJECT');
$event = (new Event())
->setSiteKey(SITE_KEY)
->setExpectedAction(PROTECTED_ACTION)
->setToken(TOKEN);
$assessment = (new Assessment())
->setEvent($event);
try {
$response = $client->createAssessment(
PARENT_PROJECT,
$assessment
);
if ($response->getTokenProperties()->getValid() == false) {
printf('The CreateAssessment() call failed because the token was invalid for the following reason: ');
printf(InvalidReason::name($response->getTokenProperties()->getInvalidReason()));
} else {
if ($response->getEvent()->getExpectedAction() == PROTECTED_ACTION) {
printf('The score for the protection action is:');
printf($response->getRiskAnalysis()->getScore());
}
else
{
printf('The action attribute in your reCAPTCHA tag does not match the action you are expecting to score');
}
}
} catch (exception $e) {
printf('CreateAssessment() call failed with the following error: ');
printf($e);
}
Here's how I got it working. Thanks to John Hanley for the help in a previous answer. The documentation had lead me to believe that (for whatever reason) Storage was required, but that was not the case: it was as simple as providing the path to the key via the credentials parameter. Not the keyFile parameter.
if (empty($_POST['g-recaptcha-response']))
die("You have failed the not-a-robot check.");
$captcha_response = $_POST['g-recaptcha-response'];
require 'composer/vendor/autoload.php';
use Google\Cloud\RecaptchaEnterprise\V1\RecaptchaEnterpriseServiceClient;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
$path_to_keyfile = "MY_PROJECT-1234567890abc.json";
$site_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$client = new RecaptchaEnterpriseServiceClient([
'credentials' => $path_to_keyfile,
'projectId' => 'MY_PROJECT'
]);
define('SITE_KEY', $site_key);
define('TOKEN', $captcha_response);
define('PROTECTED_ACTION', 'signup');
define('PARENT_PROJECT', 'projects/MY_PROJECT');
$event = (new Event())
->setSiteKey(SITE_KEY)
->setExpectedAction(PROTECTED_ACTION)
->setToken(TOKEN);
$assessment = (new Assessment())
->setEvent($event);
try {
$response = $client->createAssessment(PARENT_PROJECT, $assessment);
if ($response->getTokenProperties()->getValid() == false) {
printf('The CreateAssessment() call failed because the token was invalid for the following reason: ');
printf(InvalidReason::name($response->getTokenProperties()->getInvalidReason()));
exit;
} else {
if ($response->getEvent()->getExpectedAction() == PROTECTED_ACTION) {
// Closer to 1 = human, to 0 = robot.
$bot_score = $response->getRiskAnalysis()->getScore();
// do what you want with the score here...
} else {
die('The action attribute in your reCAPTCHA tag does not match the action you are expecting to score');
}
}
} catch (exception $e) {
printf('CreateAssessment() call failed with the following error: ');
printf($e);
exit;
}
Your problem is that you are not specifying the service account to use in the client constructor and the system is falling back to using ADC (Application Default Credentials).
ADC will check the environment variable GOOGLE_APPLICATION_CREDENTIALS for the service account JSON key file.
You can set the environment variable before running your program:
Windows:
set GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Linux:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Or modify your program by changing this line of code:
$client = new RecaptchaEnterpriseServiceClient();
To this:
$options = ['keyFile' => $path_to_keyfile];
$client = new RecaptchaEnterpriseServiceClient($options);
Note 1:
If you are running your program on a Google Cloud computer service such as Compute Engine, App Engine, Cloud Run, ... the default service account will be used if neither of the above methods are implemented.
Note 2:
While developing, another method is to use the CLI's application default credentials. Run the following command using the Google Cloud SDK CLI:
gcloud auth application-default login
However, I have not verified that the reCAPTCHA Enterprise library checks for this type of credential.

Keycloak password policy

So, I have integrated keycloak API endpoints in my Symfony project..
Regarding https://ultimatesecurity.pro/post/password-policy/ we have added 'not username' policy to test on creating new user within the app.
Idea is to delcare specific method with defined endpoint which will do this.
I was checking the documentation and could not find any endpoint that can check for password policy rules --> documentation
Idea for it:
$options = [
'headers' => $this->getAuthJsonHeaders()
];
try {
$endpoint = sprintf('auth/admin/realms/%s/', $this->realm);
$response = $this->request('GET', $endpoint, $options);
return $response;
} catch (\Exception $e) {
$this->exception('Can`t reset user password on Keycloak. ' . $e->getMessage());
}
This is what I get:
when dumping results
To get the list of the password policies being used by the Realm, you should call the following endpoint:
GET <KEYCLOAK_HOST>/auth/admin/realms/<YOUR_REALM>
from the JSON response extract the field:
passwordPolicy
which for instance if you have set Minimum length to 12 and Hashing Iterations to 27500 the passwordPolicy would be "length(12) and hashIterations(27500)"

Errors when retrieving user details from Microsoft Graph using oauth2-microsoft

I am using oauth2-microsoft to develop a 'sign in with Microsoft' tool for my app. I'm successfully authenticating and receiving a token, but then I receive an error from the sample code.
I am using the sample code below and have tried various combinations of URLs in the 'urlResourceOwnerDetails' field, including leaving it blank.
$provider = new \Stevenmaguire\OAuth2\Client\Provider\Microsoft([
'clientId' => '<redacted>',
'clientSecret' => '<redacted>',
'redirectUri' => 'http://localhost/test.php',
'urlAuthorize' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
'urlAccessToken' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v1.0/me/drive'
]);
$options = [
'scope' => ['wl.basic', 'wl.signin']
];
After this comes authentication and token generation.
Then this line throws errors:
$user = $provider->getResourceOwner($token);
A token is definitely being generated, as I can echo $token and see it.
The above code should create a $user object that contains details about the logged in user. However, instead it generates these errors:
If 'urlResourceOwnerDetails' is set to https://graph.microsoft.com/v1.0/me/drive I get:
League\OAuth2\Client\Provider\Exception\IdentityProviderException: Access token is empty
If 'urlResourceOwnerDetails' is set to https://outlook.office.com/api/v2.0/me I get:
UnexpectedValueException: Invalid response received from Authorization Server. Expected JSON.
And if 'urlResourceOwnerDetails' is empty I get:
GuzzleHttp\Exception\RequestException: cURL error 3: malformed (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Any ideas, please?
It appears oauth2-microsoft does not support Microsoft Graph Auth to a full extent at the moment, refer for example this thread
Regarding the error
League\OAuth2\Client\Provider\Exception\IdentityProviderException:
Access token is empty
access token is expected to be passed as Authorization header but according to Microsoft.php provider implementation it is passed instead as query string:
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
$uri = new Uri($this->urlResourceOwnerDetails);
return (string) Uri::withQueryValue($uri, 'access_token', (string) $token);
}
The way how library is designed, the following provider class could be introduced to support Microsoft Graph calls (by including access token in the Authorization header of a request)
class MicrosoftGraphProvider extends AbstractProvider
{
/**
* Get provider url to fetch user details
*
* #param AccessToken $token
*
* #return string
*/
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
return 'https://graph.microsoft.com/v1.0/me';
}
protected function getAuthorizationHeaders($token = null)
{
return ['Authorization'=>'Bearer ' . $token->getToken()];
}
public function getBaseAuthorizationUrl()
{
return 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
}
public function getBaseAccessTokenUrl(array $params)
{
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
}
protected function getDefaultScopes()
{
return ['openid profile'];
}
protected function checkResponse(\Psr\Http\Message\ResponseInterface $response, $data)
{
// TODO: Implement checkResponse() method.
}
protected function createResourceOwner(array $response, AccessToken $token)
{
return (object)$response;
}
}

Build correct SOAP Request Header (PHP)

I have to do requets to a SOAP API with PHP and I need the following SOAP-Header structure:
<soapenv:Header>
<ver:authentication>
<pw>xxx</pw>
<user>xxx</user>
</ver:authentication>
</soapenv:Header>
How can I build this header?
I tried
$auth = [
"ver:authentication" => [
"pw" => $this->pw,
"user" => $this->user
]
];
$options = [];
$options["trace"] = TRUE;
$options["cache_wsdl"] = WSDL_CACHE_NONE;
$options["compression"] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
$client = new SoapClient("www.my-url.com/wsdl", $options);
$header = new SoapHeader("www.my-url.com", "authentication", $auth, false);
$client->__setSoapHeaders($header);
but it does not work. The respons is "failure" which I get, when the header structure is incorrect...
please help
the solution could be object driven. In the following code an example is given. Please keep in mind, that the following code is not testet.
class Authentication
{
protected $user;
protected $pw;
public function getUser() : ?string
{
return $this->user;
}
public function setUser(string $user) : Authentication
{
$this->user = $user;
return $this;
}
public function getPw() : string
{
return $this->pw;
}
public function setPw(string $pw) : Authentication
{
$this->pw = $pw;
return $this;
}
}
The above shown class is a simple entity, which contains two properties $user fpr the username and $pw for the password. Further it contains the getter and setter functions for retrieving or setting the values for the two properties.
For the next step just fill the class with data and store it in a SoapVar object.
$authentication = (new Authentication())
->setUser('Username')
->setPw('YourEncodedPassword');
$soapEncodedObject = new \SoapVar(
$authentication,
SOAP_ENC_OBJECT,
null,
null,
'authentication',
'http://www.example.com/namespace'
);
As you can see above, your authentication class will be stored as soap var object. It is encoded as soap object. The only thing you have to do is setting the namespace for this object. In your given example it is ver:. With this namespace prefix somewhere in your wsdl file a namespace is noted. You have to find out this namespace url and just replace the example url http://www.example.com/namespace with the right url noted in your wsdl.
The next step is setting this as soap header. That 's quite simple.
try {
$client = new SoapClient('http://www.example.com/?wsdl', [
'trace' => true,
'exception' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
]);
// set the soap header
$header = new SoapHeader('http://www.example.com/namespace', 'authentication', $authentication, false);
$client->setSoapHeaders($header);
// send the request
$result = $client->someWsdlFunction($params);
} catch (SoapFault $e) {
echo "<pre>";
var_dump($e);
echo "</pre>";
if ($client) {
echo "<pre>";
var_dump($client->__getLastRequest());
echo "</pre>";
echo "<pre>";
var_dump($client->__getLastResponse());
echo "</pre>";
}
}
As you can see it 's a bit different from your given example. Instead of an array it 's the soap encoded authentication object, that is given to the soap header class. For failure purposes there is a try/catch block around your soap client. In that case you can identify the error and if the client was initiated correctly, you can also see the last request and last response in xml.
I hope, that I helped you. ;)
I would strongly advise you 2 things:
Use a WSDL to PHP generator in order to properly construct your request. In addition, it will ease you the response handling. Everything is then using the OOP which is much better. Take a look to the PackageGenerator project.
Use the WsSecurity project in order to easily add your dedicated SoapHeader without wondering how to construct it neither.

Categories