Symfony2 Google API, How to use Google client - php

I am using Symfony2.3 and I want to access Google Calendar API, here what i did
1-I installed HIWO Bundle and FOSUser Bundle
2-Integrated both bundles and now i have user authenticated and inserted into database with access token
3-I have installed Google API library and auto-loaded it
4-created a Service wrapper class to access
Problem 1 :
Seems now i m using Oauth2 found in HIWO Bundle while logging in and I will be using Oauth2 in Google API library while making request, which dosent make any sense and not sure what should be done in this matter
Trials:
-I found out that token provided by HIW Oauth is not the same as the one in code parameter in URL while redirecting back
-Tried to set token manually and try to intiat simulate Google client request $cal = new \Google_Calendar($this->googleClient) as below but
$this->googleClient->authenticate('4/PmsUDPCbxWgL1X_akVYAhvnVWqpn.ErqFdB3R6wMTOl05ti8ZT3Zpgre8fgI');
return $cal->calendarList->listCalendarList();`
Error received:
Error fetching OAuth2 access token, message: 'redirect_uri_mismatch'
and i made sure i have redirect_uri matched
My Service code is as below :
<?php
namespace Clinic\MainBundle\Services;
use Clinic\MainBundle\Entity\Patient;
use Doctrine\Common\Persistence\ObjectManager;
/*
* #author: Ahmed Samy
*/
class GoogleInterfaceService {
/*
* Entity manager
*/
protected $em;
/*
* instance of Symfphony session
*/
protected $session;
/*
* Service container
*/
protected $container;
/*
* Google client instance
*/
protected $googleClient;
public function __construct(ObjectManager $em, $container) {
$this->em = $em;
$this->container = $container;
$this->googleClient = new \Google_Client();
$this->googleClient->setClientId('xxxxxxxx.apps.googleusercontent.com');
$this->googleClient->setClientSecret('uNnaK1o-sGH_pa6Je2jfahpz');
$this->googleClient->setRedirectUri('http://hacdc.com/app_dev.php/login/check-google');
$this->googleClient->setDeveloperKey('xxxxxxxxxxxxxxxxxxxx');
$this->googleClient->setApplicationName("Google Calendar PHP Starter Application");
}
public function getCalendar() {
$cal = new \Google_Calendar($this->googleClient);
//setting token manually
$this->googleClient->authenticate('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
return $cal->calendarList->listCalendarList();
}
}
and when i dump $this->googleClient i get
protected 'scopes' =>
array (size=0)
empty
protected 'useObjects' => boolean false
protected 'services' =>
array (size=0)
empty
private 'authenticated' => boolean false

The HWIOAuthBundle's token is missing the created array segment, but you could force that in there, and then just feed that token to the client like so:
$googleAccessToken = $this->get('security.context')->getToken()->getRawToken();
$googleAccessToken['created'] = time(); // This is obviously wrong... but you get the poing
$this->google_client->setAccessToken(json_encode($googleAccessToken));
$activities = $this->google_plusservice->activities->listActivities('me', 'public');
var_dump($activities);die();

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 to access G Suite member's calendar by service account

I wanna call Google Calendar API's freebusy.query endpoint by service account in server-side with php and google-api-php-client.
First, I opened Google Cloud Platform project page. Then I created service account, downloaded credential JSON file, enabled Calendar API, and allowed delegated authority.
Second, I opened Google Admin. Then I allowed scope:https://www.googleapis.com/auth/calendar for delegated authority.
Third, I wrote the following code. There're some laravel helper functions. I referenced https://github.com/googleapis/google-api-php-client/blob/master/examples/service-account.php to use library for service account.
<?php
use DateTime;
use Google_Client;
use Google_Service_Calendar;
use Google_Service_Calendar_FreeBusyRequest as FreeBusyRequest;
use Google_Service_Calendar_FreeBusyRequestItem as FreeBusyRequestItem;
$calendar = new Calendar();
$busyPeriods = $calendar->getBusyTimes(
[env('GSUITE_MEMBER')],
'2021-03-01 00:00:00',
'2021-04-01 00:00:00'
);
dd($busyPeriods);
class Client {
protected $client;
public function __construct() {
$this->client = new Google_Client();
$credentialsPath = env('GOOGLE_SA_CREDENTIALS');
if (file_exists($credentialsPath)) {
$this->client->setAuthConfig($credentialsPath);
} else {
error_log('Google service account credentials don\'t exist.');
return;
}
}
public function setScopes($scopes) {
$this->client->setScopes($scopes);
}
public function getClient() {
return $this->client;
}
}
class Calendar {
protected $service;
public function __construct() {
$client = new Client();
$client->setScopes([Google_Service_Calendar::CALENDAR]);
$this->service = new Google_Service_Calendar($client->getClient());
}
/**
* #param array $calendarIdList List of calendar identifier.
* #param string $start Start of time period for getting busy times.
* #param string $end End of time period for getting busy times.
* #return Google_Service_Calendar_FreeBusyCalendar[]
*/
public function getBusyTimes($calendarIdList, $start, $end) {
$fbRequestItems = collect($calendarIdList)->map(function ($calendarId) {
$i = new FreeBusyRequestItem();
$i->setId($calendarId);
return $i;
})->toArray();
$fbRequest = new FreeBusyRequest();
$fbRequest->setItems($fbRequestItems);
$fbRequest->setTimeZone(config('app.timezone'));
$fbRequest->setTimeMin(
(new DateTime($start))->format(DateTime::ISO8601)
);
$fbRequest->setTimeMax(
(new DateTime($end))->format(DateTime::ISO8601)
);
return $this->service->freebusy->query($fbRequest)->getCalendars();
}
}
However, Google_Service_Calendar_Error was returned.
Google_Service_Calendar_Error {#347 ▼
+domain: "global"
+reason: "notFound"
#internal_gapi_mappings: []
#modelData: []
#processed: []
}
We should impersonate G Suite member's account.
$this->client->setSubject(
config('google.gsuite_admin_email')
);
Reference
Domain Wide Delegation
Google OAuth using domain wide delegation and service account

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;
}
}

Amazon ElasticSearch service Signature mismatch for PUT Request - Amazon SDK php V2

I am using Amazon ElasticSearch Service and when i tried to create SignatureV4 Request it is working fine for search operations (GET Requests). But when i tried to do some operations like create indices (Using PUT request), it will trough the Signature mismatch error.
I am using Amazon SDK version 2 SignatureV4 library for signing the requests. Also created a custom Elasticsearch handler to add tokens to the request.
Does anybody have such issue with SignatureV4 library in Amazon SDK php V2.
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'PUT\n/test_index_2\n\nhost:search-test-gps2gj4zx654muo6a5m3vxm3cy.eu-west-1.es.amazonaws.com\nx-amz-date:XXXXXXXXXXXX\n\nhost;x-amz-date\n271d5ef919251148dc0b5b3f3968c3debc911a41b60ef4e92c55b98057d6cdd4'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\XXXXXXXXXXXX\n20170511/eu-west-1/es/aws4_request\n0bd34812e0727fba7c54068b0ae1114db235cfc2f97059b88be43e8b264e1d57'\n"}
This tweak only necessary for the users who are still using Amazon SDK PHP version 2. In version 3, it supported by default.
For signed request i updated the current elsticsearch client handler by adding a middle ware for signing the request.
$elasticConfig = Configure::read('ElasticSearch');
$middleware = new AwsSignatureMiddleware();
$defaultHandler = \Elasticsearch\ClientBuilder::defaultHandler();
$awsHandler = $middleware($defaultHandler);
$clientBuilder = \Elasticsearch\ClientBuilder::create();
$clientBuilder->setHandler($awsHandler)
->setHosts([$elasticConfig['host'].':'.$elasticConfig['port']]);
$client = $clientBuilder->build();
I used the following library for this purpose
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Signature\SignatureInterface;
use Guzzle\Http\Message\Request;
class AwsSignatureMiddleware
{
/**
* #var \Aws\Credentials\CredentialsInterface
*/
protected $credentials;
/**
* #var \Aws\Signature\SignatureInterface
*/
protected $signature;
/**
* #param CredentialsInterface $credentials
* #param SignatureInterface $signature
*/
public function __construct()
{
$amazonConf = Configure::read('AmazonSDK');
$this->credentials = new \Aws\Common\Credentials\Credentials($amazonConf['key'], $amazonConf['secret']);
$this->signature = new \Aws\Common\Signature\SignatureV4('es', 'eu-west-1');
}
/**
* #param $handler
* #return callable
*/
public function __invoke($handler)
{
return function ($request) use ($handler) {
$headers = $request['headers'];
if ($headers['host']) {
if (is_array($headers['host'])) {
$headers['host'] = array_map([$this, 'removePort'], $headers['host']);
} else {
$headers['host'] = $this->removePort($headers['host']);
}
}
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
$psrRequest = new Request($request['http_method'], $request['uri'], $headers);
$this->signature->signRequest($psrRequest, $this->credentials);
$headerObj = $psrRequest->getHeaders();
$allHeaders = $headerObj->getAll();
$signedHeaders = array();
foreach ($allHeaders as $header => $allHeader) {
$signedHeaders[$header] = $allHeader->toArray();
}
$request['headers'] = array_merge($signedHeaders, $request['headers']);
return $handler($request);
};
}
protected function removePort($host)
{
return parse_url($host)['host'];
}
}
The exact line i tweaked for this purpose is
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
For PUT and POST request the payload hash was wrong because i was not considering the request body while generating payload.
Hope this code is beneficial for anyone who is using Amazon SDK PHP version 2 and using the IAM based authentication for Elasticsearch Hosted service in Amazon cloud.

PHP Google Drive API installation and file upload

Hi guys i'm trying uploading file trought G drive API.
Can't find out why it returns error:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Gdrive{
function initialize(){
$credentials = $this->GetOAuth2Credentials($_GET['code']);
$_SESSION['credentials'] = $credentials;
}
/**
* Exchange an authorization code for OAuth 2.0 credentials.
*
* #param String $authorizationCode Authorization code to exchange for an
* access token and refresh token. The refresh token is only returned by
* Google on the very first exchange- when a user explicitly approves
* the authorization request.
* #return OauthCredentials OAuth 2.0 credentials object
*/
function GetOAuth2Credentials($authorizationCode) {
$client = new apiClient();
$client->setClientId(Config::5112+++++.apps.****5971157#developer.gserviceaccount.com);
$client->setRedirectUri(Config::site_url());
/**
* Ordinarily we wouldn't set the $_GET variable. However, the API library's
* authenticate() function looks for authorization code in the query string,
* so we want to make sure it is set to the correct value passed into the
* function arguments.
*/
$_GET['code'] = $authorizationCode;
$jsonCredentials = json_decode($client->authenticate());
$oauthCredentials = new OauthCredentials(
$jsonCredentials->access_token,
isset($jsonCredentials->refresh_token)?($jsonCredentials->refresh_token):null,
$jsonCredentials->created,
$jsonCredentials->expires_in,
Config::CLIENT_ID,
Config::CLIENT_SECRET
);
return $oauthCredentials;
}
function SaveNewFile($inputFile) {
try {
$mimeType = 'text/plain';
$file = new Google_DriveFile();
$file->setTitle($inputFile->title);
$file->setDescription($inputFile->description);
$file->setMimeType($mimeType);
// Set the parent folder.
if ($inputFile->parentId != null) {
$parentsCollectionData = new DriveFileParentsCollection();
$parentsCollectionData->setId($inputFile->parentId);
$file->setParentsCollection(array($parentsCollectionData));
}
$createdFile = $this->service->files->insert($file, array(
'data' => $inputFile->content,
'mimeType' => $mimeType,
));
return $createdFile;
} catch (apiServiceException $e) {
/*
* Log error and re-throw
*/
error_log('Error saving new file to Drive: ' . $e->getMessage(), 0);
throw $e;
}
}
}
when i invoke the initialize() method it returns error:
Message: Undefined index: code
Fatal error: Class 'apiClient' not found
what should be? i'm doing right in my code ? does i need more code to make it works? i created web application project on google api console.
need i to include google php sdk? in the google docs it is not mentioned for google drive api :/
You are probably using an older version of the PHP client library. Make sure you have the latest source and follow the instructions in the Google Drive SDK quickstart page to learn how to write a complete PHP app to upload a file to Drive:
https://developers.google.com/drive/quickstart
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
$client = new Google_Client();
please use those require files and Google_Client().

Categories