On Getting Permission for to Access User's Email - php

I am using Google APIs PHP client to to access the Google Drive API.
My problem is that I want to get permission to access user email. However the authenticate screen doesn't show the permission I am seeking. It just display "Application would like to ....Have offline access. But when done from the Google API playground, it correctly displays access to users email prompt.
So I think there is something wrong in my code level while creating authenticate url. Please help me to resolve the issue.
$client = new Google_Client();
$guzzleClient = new \GuzzleHttp\Client(array( 'curl' => array( CURLOPT_SSL_VERIFYPEER => false, ), ));
$client->setHttpClient($guzzleClient);
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setState($this->uri->segment(3));
$client->addScope("https://www.googleapis.com/auth/drive");

If all you want is the email address of the current authencated user you can simply go though the google drive api.
About.get method Gets information about the user, the user's Drive, and system capabilities.
{
"user": {
"kind": "drive#user",
"displayName": "Linda Lawton",
"photoLink": "xxxx",
"me": true,
"permissionId": "xxxx",
"emailAddress": "xxxx#gmail.com"
}
}
Side note on scope:
Scopes are what tells the user what data you want to access. https://www.googleapis.com/auth/drive basically says you want full access to their Google drive account https://www.googleapis.com/auth/classroom.profile.emails would request access to their email address.
Offline access
'offline' tells the user you want to be able to access their account when they are not using your application. By adding this you will receive a refresh token which you can save and use at anytime to get a new access token to access a users data.

Related

403 error access denied even with correct perissions using Microsoft Graph api with the PHP SDK when accessing calendar events

I am having trouble accessing calendar events of users using the Microsoft Graph api and get a access denied response even though I have set all of the correct permissions and more, both application and delegated for (Calendars.Read Delegated, Calendars.Read Application, Calendars.Read.Shared Delegated, Calendars.ReadBasic Delegated, Calendars.ReadBasic.All Application, Calendars.ReadWrite Delegated, Calendars.ReadWrite Application, Calendars.ReadWrite.Shared Delegated). I am using the microsoft developer instant sandbox users who all have Microsoft 365 E5 Developer licences and all have Exchange Online Plan 2.
The code I use to retrieve the access token and send the calendar request is below:
$guzzle = new \GuzzleHttp\Client();
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/v2.0/token';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'scope' => 'https://graph.microsoft.com/.default',
'grant_type' => 'client_credentials',
],
])->getBody()->getContents());
$accessToken = $token->access_token;
$graph = new Graph();
$graph->setBaseUrl("https://graph.microsoft.com/")
->setAccessToken($accessToken);
$ptr = $graph->createCollectionRequest('GET', '/users/{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}/events')
->setPageSize(100);
If I use the same code with the endpoint /users/{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}/calendars I get a list of the users calendars but as soon as I try to get events I get a 403 error. The same issue occours if I use the deligated auth flow and try to access events as a signed in user, again I am able to access a list of calendars.
When decoding the access token the roles section looks like this:
"roles": [
"Calendars.Read",
"Mail.ReadBasic.All",
"Group.Read.All",
"User.Read.All",
"Calendars.ReadBasic.All",
"GroupMember.Read.All",
"Calendars.ReadWrite",
"Mail.Send"
]
Are there other permissions that need to be set for an app or a user to make this work?
Let me know if I can add any more info to help with answers, sny sugesstions would be helpful
Thanks
UPDATE
Having looked further it could be an issue with "Limiting application permissions to specific Exchange Online mailboxes" (https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access)
There was a policy defined but having removed this (and waiting for that update to become effective) I am still getting the same error. I assume therefore that a policy is needed to allow access however I can't find any documentation about this.
So the fix (given here https://learn.microsoft.com/en-us/answers/questions/1167148/microsoft-graph-throwing-403-error-for-calendar-wi) is to only have the lowest level permission, otherwise you get the error response.

Google Calendar Api. cannot connect to other user calendar

I understand that there were already similar questions here, but the answers to them did not help me.
Trying to connect through a service account to the calendars of users who are part of the domain. For connection I use Google API PHP Client (https://github.com/googleapis/google-api-php-client)
Account setup followed the instructions (https://developers/admin-sdk/directory/v1/guides/delegation)
Here's my code:
<?php
require '/vendor/autoload.php';
$client = new Google_Client();
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json');
$client->useApplicationDefaultCredentials();
$client->authorize();
$scopes = implode(' ', [Google_Service_Calendar::CALENDAR, Google_Service_Calendar::CALENDAR_EVENTS]);
$client->setScopes($scopes);
$client->setSubject('user#email.com');
$service = new Google_Service_Calendar($client);
$optParams = [
'maxResults' => 10,
'orderBy' => 'startTime',
'singleEvents' => TRUE,
'timeMin' => date('c')
];
$results = $service->events->listEvents('root#email.com', $optParams);
$events = $results->getItems();
And then I get an authorization error:
Error: {
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested."
}
Can you tell me if someone 's already faced a mistake like this? Should the root#email.com be mail or mail that was generated during access setup? Or is the problem somewhere else at all?
Thank you in advance for any help!
Your code is actually correct. Be sure you authorized your service account to use the same scopes you are trying to use in your code. This is a common issue with service accounts.
Here a walk-through from the docs:
Go to your G Suite domain’s Admin console.
Select Security from the list of controls. If you don't see Security listed, select More controls from the gray bar at the bottom of the page, then select Security from the list of controls.
Select Advanced settings from the list of options.
Select Manage API client access in the Authentication section.
In the Client name field, enter the client ID obtained from the service account creation steps above.
In the One or More API Scopes field enter the scopes required for your application (for a list of possible scopes, see Authorize requests).
For example, if you require domain-wide access to Calendar and Calendar Events: https://www.googleapis.com/auth/calendar, https://www.googleapis.com/auth/calendar.events
Click the Authorize button.

Access to data using Google AdSense Management API - PHP

I'm trying to retrieve data from an AdSense account through the AdSense Management API.
I have successfully retrieved data using Google APIs Explorer when connected with my AdSense account.
I am now trying to get this data from a PHP script which will be run in a crontab.
My understanding is that I need a Service account which I created and I generated a json file.
The following code fails if I try to use the json file with two different errors :
if I use the setSubject with the owner of the AdSense account :
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method."
If I comment the setSubject :
"reason": "noAdSenseAccount",
"message": "User does not have an AdSense account."
Code:
<?php
require_once '../../vendor/autoload.php';
putenv('GOOGLE_APPLICATION_CREDENTIALS=../conf/Mercury-testlpstats.json');
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
//$client->setSubject('AdTech#Lagardere-Pub.com');
$service = new Google_Service_AdSense($client);
$startDate = '2017-05-01';
$endDate = '2017-05-01';
$optParams = array(
'metric' => array('INDIVIDUAL_AD_IMPRESSIONS', 'EARNINGS'),
'dimension' => 'DATE',
'filter' => array('AD_UNIT_ID==ca-pub-XXXX:YYYY'),
'useTimezoneReporting' => true
);
$report = $service->accounts_reports->generate('pub-ZZZZZ', $startDate, $endDate, $optParams);
2nd attempt with OAuth
I've created a OAuth account in https://console.cloud.google.com/apis/credentials/oauthclient/. When I check the AdSense API, my account appears in the list of authorized users in https://console.cloud.google.com/apis/api/adsense.googleapis.com/overview.
I've downloaded the corresponding JSON and changed my code but it still says:
"insufficientPermissions" if I set $client->useApplicationDefaultCredentials();
"Login Required" otherwise
require_once '../../vendor/autoload.php';
$client = new Google_Client();
$client->setAccessType('online');
$client->setAuthConfigFile('/home/al1/lpstats/conf/client_secret_725834039890-klbuc13f8089rjh7eis439b93n7sqqfv.apps.googleusercontent.com.json');
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
$service = new Google_Service_AdSense($client);
$startDate = '2017-05-01';
$endDate = '2017-05-01';
$optParams = array(
'metric' => array('INDIVIDUAL_AD_IMPRESSIONS', 'EARNINGS'),
'dimension' => 'DATE',
'filter' => array('AD_UNIT_ID==ca-pub-5035025648894332:3442683203'),
'useTimezoneReporting' => true
);
$report = $service->accounts_reports->generate('pub-5035025648894332', $startDate, $endDate, $optParams);
unauthorized_client
Means that the user you are authencating with does not have access to the Adsence Account you are trying to access.
Service account support
There are a number of Google APIs which don't support service accounts. Most noticeably would be YouTube API which does not even have a way of sharing your account with another user.
There are others which will allow you to share your data with another user but require that the user in question respond to an email notification. Blogger is one and Adsence is apparently another.
Solution
You will need to authenticate using Oauth2 using a user who has access to the adsence account in question. Authenticate your code once save the refresh token and use the refresh token to request a new access token within your cron job.

Cannot connect with a Google Service Account via php and google apiclient alpha 2.0.0

I'm having a problem connecting through the google apiclient 2.0.0 as installed via composer.
Here's what I've done so far:
I installed apiclient via composer.
I went to https://console.cloud.google.com/apis/credentials?my-project and navigated to the API manager > Credentials page.
I created credentials for a Service Account and downloaded the json. I then uploaded the json to my server.
I enabled Domain Wide Delegation for the account by managing my service accounts.
I verified my domain name under the 'Domain Verification' tab.
Next I navigated to my calendar and shared the calendar with the email address associated with my service account, granting manage access.
Then I shared my calendar with the email address of the service account.
Now I'm trying to use the api to connect and running into authorization problems.
$json_file = '/path/to/service-account-credentials.json';
$scopes = [
Google_Service_Calendar::CALENDAR,
Google_Service_Calendar::CALENDAR_READONLY
];
// create a new client and authorize
$client = new Google_Client();
// set the basic information of the client
$client->setApplicationName("Apointments");
$client->setSubject( email-address-to-masquerade-as#mycompany.com );
$client->setAccessType('offline');
// load the json credential file
$client->setAuthConfig( $json_file );
$client->setScopes( $scopes );
// check to see if the token is stored in the session
if( isset( $_SESSION['service_token'] ) )
{
// token was stored, use it with the cilent
$client->setAccessToken( $_SESSION['service_token'] );
}
// test the authorization to see if it is expired
if( $client->isAccessTokenExpired() )
{
// Failed authorization, get a new token
$client->refreshTokenWithAssertion();
}
// store the token in the session
$_SESSION['service_token'] = $client->getAccessToken();
From a stacktrace, I've identified the offending line of code to be:
$client->refreshTokenWithAssertion();
Which yields an error message:
Client error response [url] https://www.googleapis.com/oauth2/v4/token [status code] 401 [reason phrase] Unauthorized
Any thoughts as to where I've gone awry?
Thanks in advance,
Adam
The way you are using Service account is only valid to access data related to your application or to your service account itself. Since you wish to access Google Calendar data, you must be a Google Apps domain administrator with privileges to access any of your domain user's data. Google Calendar data belong to users so you need to specify which user you are trying to access.
You need to authenticate the user, need to get an access token and pass that. If you are getting an access token, you can call token info passing access token to get more information about who the user is associated with it.
Here's a Quick start demo app, use this as your reference: https://developers.google.com/+/web/samples/php

Google Apps Marketplace SSO: offline access

Our application is widely-integrated with Google services like Mail, Contacts, Calendar etc. And for all of these Google services we need access to the user's data permanently, without asking the user for permission every time we need to pull our app with data from Google. It's very important to have instant access to the user's data when we sync contacts, calendar or access mail via Google IMAP.
We also provide access to our app from any Google service via Single-Sign-On (SSO). And here we have a trouble.
We are asking users who install our application for "offline" access to their data. So we can have access to their data instantly, when we run our sync scripts etc.
First time user grants access to our app (via SSO, when it clicks on our app icon), it's asked to grant access to all data API we need - contacts, email, calendar etc. Everything goes okay and user gets logged in our app. But when user accesses our app next times (same via SSO), it gets asked to grant "Have offline access". It's very strange, because first time user doesn't get asked for this. And it also doesn't look nice to ask user every time for "offline access". Why we consider such behaviour strange is because once user has already granted access. So why it gets asked again and again?
The code we are using for SSO is as follows:
$clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com";
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$callback = "https://our_domain/callback";
$scopes = array(
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/drive.readonly.metadata",
"https://www.googleapis.com/auth/calendar",
"https://www.google.com/m8/feeds",
"https://www.googleapis.com/auth/tasks",
"https://www.googleapis.com/auth/admin.directory.user.readonly",
"https://mail.google.com/"
);
// $params is a list of GET and POST parameters
if (empty($params['code']))
{
$_SESSION['GOOGLE_SSO_STATE'] = md5(uniqid(rand(), true));
$client = new Google_Client();
$client->setClientId($clientId);
$client->setRedirectUri($callback);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setState($_SESSION['GOOGLE_SSO_STATE']);
$client->setScopes($scopes);
$url = $client->createAuthUrl();
echo "<script> top.location.href='" . $url . "'</script>";
exit;
}
if (!empty($params['code']) && !empty($params['state']) && $params['state'] == $_SESSION['GOOGLE_SSO_STATE'])
{
unset($_SESSION['GOOGLE_SSO_STATE']);
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($callback);
$credentials = $client->authenticate();
// we need refresh token so we can exchange it for new access token when the current ones expires
if (!isset($credentials_['refresh_token']))
{
echo "Wrong credentials received";
exit;
}
$client = new Google_Client();
$client->setUseObjects(true);
$client->setAccessToken($credentials);
$userInfoService = new Google_Oauth2Service($client);
$userInfo = $userInfoService->userinfo->get();
echo $userInfo->getId();
}
Can anyone help us understand this behaviour? Or maybe someone even knows how to make it not asking user for "offline access" every time it accesses the app?
This because your approval prompt is set to "force:
$client->setApprovalPrompt('force');
Use force only if you need to re-issue a refresh token. There is a strange side effect that ask the user time and again for offline access.
We use Service Accounts which provide us with offline access for any user in the installed domain (for the chosen Organiztaion Unit). Depending on what you are trying to do, this may be an option for you.
https://developers.google.com/drive/web/service-accounts

Categories