I have attempted many solutions provided on Stack Exchange to obtain a refresh token, and they all fail. Below is my whole controller. As you can see, I have included $client->setAccessType('offline') and $client->setApprovalPrompt('force').
In addition, I have ran the revoke token many times, and I have gone into my Google Account here (https://myaccount.google.com/permissions), and removed access. None of these attempts provided me with a refresh token when I made a new authorization.
<?php
/**
* #file
* Contains \Drupal\hello\GAOauthController.
*/
namespace Drupal\ga_reports_per_user\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Google_Client;
use Google_Service_Analytics;
use Drupal\group\Context\GroupRouteContextTrait;
class GAOauthController extends ControllerBase {
use GroupRouteContextTrait;
public function authorize($group = NULL) {
$client = new Google_Client();
$credentials_file = \Drupal::service('file_system')->realpath("private://") . '/client_credentials.json';
$client->setAuthConfig($credentials_file);
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
$protocol = "https";
}
else {
$protocol = "http";
}
$redirect_uri = $protocol . '://' . $_SERVER['HTTP_HOST'] . '/finish_google_oauth';
$client->setState($group);
$client->setRedirectUri($redirect_uri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$auth_url = $client->createAuthUrl();
return new TrustedRedirectResponse($auth_url);
}
public function finishOauth() {
if (isset($_GET['code'])) {
$client = new Google_Client();
$credentials_file = \Drupal::service('file_system')->realpath("private://") . '/client_credentials.json';
$client->setAuthConfig($credentials_file);
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
$protocol = "https";
}
else {
$protocol = "http";
}
$redirect_uri = $protocol . '://' . $_SERVER['HTTP_HOST'] . '/finish_google_oauth';
$client->setRedirectUri($redirect_uri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->authenticate($_GET['code']);
$token = $client->getAccessToken();
$refreshToken = $client->getRefreshToken();
$client->setAccessToken($token);
$group_entity = \Drupal::entityTypeManager()->getStorage('group')->load($_GET['state']);
$group_entity->field_google_oauth_token->value = $token['access_token'];
$group_entity->field_google_oauth_token_type->value = $token['token_type'];
$group_entity->field_google_oauth_token_created->value = $token['created'];
$group_entity->field_google_oauth_token_expire->value = $token['expires_in'];
$save_status = $group_entity->save();
return new RedirectResponse('/group/' . $_GET['state']);
}
}
public function revoke($group = NULL){
$client = new Google_Client();
$group_entity = \Drupal::entityTypeManager()->getStorage('group')->load($group);
$result = $client->revokeToken($group_entity->field_google_oauth_token->value);
$group_entity->field_google_oauth_token->value = '';
$group_entity->field_google_oauth_token_type->value = '';
$group_entity->field_google_oauth_token_created->value = '';
$group_entity->field_google_oauth_token_expire->value = '';
$group_entity->save();
return new RedirectResponse('/group/' . $group);
}
}
If you have already requested an access token for that login / dev. token combo, it won't return it again for you. You'll have to revoke access by going to https://myaccount.google.com/permissions. Find your app, revoke access, and try again.
Source: I had this exact same problem and after nearly pulling my hair out, I figured this out.
Related
The youtube likes/subscriptions appear in the user account, but not in the video/channel
I'm making a webpage where people accepts youtube permissions (using manage youtube account scope) and they can like videos or subscribe to channels through the web.
With likes:
- the user see that has liked the video
- noone never sees the total likes going up
With subscriptions the issue is:
- at the beginning the user can see he is subscribed to the channel
- after few hours everyone can see the increased number of subscribers
- some time later (like one day or more), the subscribers decreased
- but the user can still see that is subscribed
Here is an image of how the user sees it after one day or more: link to image
The code I'm using for likes is:
<?php
require_once 'Google/Client.php';
require_once 'Google/autoload.php';
require_once 'Google/Service/YouTube.php';
session_start();
$client_id= $_POST['client'];
$client_secret= $_POST['secret'];
$refreshtoken = $_POST['ref'];
$idvideo = $_POST['idvideo'];
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$client->refreshToken($refreshtoken );
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'], FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
$youtube = new Google_Service_YouTube($client);
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$_SESSION['token'] = $client->getAccessToken();
header('Location: ' . $redirect);
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
if ($client->getAccessToken()) {
try {
$youtube->videos->rate($idvideo, "like");
// It always arrives here
} catch (Exception $e) {
//
}
$_SESSION['token'] = $client->getAccessToken();
} else {
// If the user has not authorized the application, start the OAuth 2.0 flow.
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
}
?>
And for subscriptions is basically the same, the only difference is in this if:
if ($client->getAccessToken()) {
try {
$resourceId = new Google_Service_YouTube_ResourceId();
$resourceId->setChannelId($idChannel);
$resourceId->setKind('youtube#channel');
$subscriptionSnippet = new Google_Service_YouTube_SubscriptionSnippet();
$subscriptionSnippet->setResourceId($resourceId);
$subscription = new Google_Service_YouTube_Subscription();
$subscription->setSnippet($subscriptionSnippet);
$subscriptionResponse = $youtube->subscriptions->insert('id,snippet', $subscription, array());
} catch (Exception $e) {
//
}
$_SESSION['token'] = $client->getAccessToken();
}
I don't know if google sees my webpage as something strange or what the problem is, but i've been in this same point for aboout one week and haven't find any similar problem.
Also the webpage doesn't appear as blacklisted on www.blacklistalert.org or mxtoolbox.com
I wish to download all data from each of my channel's analytics reports minute by minute but I am getting a 403 error. What sort of permissions do I need? How do I get access to the Analytics Reporting API? It says that the API is not available for me.
Additionally will the server 2 server auth work for getting data on a regular bases (at least 10 times a day)?
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
/*
* This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
* authenticated user's account. Any request that retrieves earnings or ad performance metrics must
* use this scope.
*/
$client->setScopes(array('https://www.googleapis.com/auth/youtube.readonly', 'https://www.googleapis.com/auth/yt-analytics.readonly'));
$redirect = filter_var('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$redirect = str_replace('.php', '', $redirect);
$client->setRedirectUri($redirect);
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
header('Location: ' . $redirect);
}
try {
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
if ($client->getAccessToken()) {
$youtube = new Google_Service_YouTube($client);
//die("<pre>" . var_export($youtube->channels->listChannels('id', array('forUsername' => 'kingbach')),1));
$analytics = new Google_Service_YouTubeAnalytics($client);
$id = 'channel==<CHANNEL_ID>';
$start_date = '2016-09-17';
$end_date = '2016-09-24';
$optparams = array(
'onBehalfOfContentOwner' => '<USERNAME>'
);
$metrics = array(
'views',
'estimatedMinutesWatched',
'averageViewDuration',
'comments',
//'favoritesAdded',
//'favoritesRemoved',
'likes',
'dislikes',
'shares',
'subscribersGained',
'subscribersLost'
);
$api_response = $metrics;
foreach ($metrics as $metric) {
$api = $analytics->reports->query($id, $start_date, $end_date, $metric, $optparams);
if (isset($api['rows'])) {
$api_response[$metric] = $api['rows'][0][0];
}
}
} else {
// If the user hasn't authorized the app, initiate the OAuth flow
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$htmlBody = <<<END
<h3>Authorization Required</h3>
<p>You need to authorize access before proceeding.<p>
END;
}
} catch (\Exception $e) {
die("<pre>" . $e->getMessage() . "</pre>");
}
You need to enable the API in the Cloud Platform Console. See https://support.google.com/cloud/answer/6158841?hl=en for details
I want to oauth authentication like
Login using Google OAuth 2.0 with C#
But i don't want to authentication prompt popup
i want to get token directly without popup..
public ActionResult CodeLele()
{
if (Session.Contents.Count > 0)
{
if (Session["loginWith"] != null)
{
if (Session["loginWith"].ToString() == "google")
{
try
{
var url = Request.Url.Query;
if (url != "")
{
string queryString = url.ToString();
char[] delimiterChars = { '=' };
string[] words = queryString.Split(delimiterChars);
string code = words[1];
if (code != null)
{
//get the access token
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://accounts.google.com/o/oauth2/token");
webRequest.Method = "POST";
Parameters = "code=" + code + "&client_id=" + googleplus_client_id + "&client_secret=" + googleplus_client_sceret + "&redirect_uri=" + googleplus_redirect_url + "&grant_type=authorization_code";
byte[] byteArray = Encoding.UTF8.GetBytes(Parameters);
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = byteArray.Length;
Stream postStream = webRequest.GetRequestStream();
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
WebResponse response = webRequest.GetResponse();
postStream = response.GetResponseStream();
StreamReader reader = new StreamReader(postStream);
string responseFromServer = reader.ReadToEnd();
GooglePlusAccessToken serStatus = JsonConvert.DeserializeObject<GooglePlusAccessToken>(responseFromServer);
if (serStatus != null)
{
string accessToken = string.Empty;
accessToken = serStatus.access_token;
if (!string.IsNullOrEmpty(accessToken))
{
// This is where you want to add the code if login is successful.
// getgoogleplususerdataSer(accessToken);
}
else
{ }
}
else
{ }
}
else
{ }
}
}
catch (WebException ex)
{
try
{
var resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
dynamic obj = JsonConvert.DeserializeObject(resp);
//var messageFromServer = obj.error.message;
//return messageFromServer;
return obj.error_description;
}
catch (Exception exc)
{
throw exc;
}
}
}
}
}
return Content("done");
}
public ActionResult JeClick()
{
var Googleurl = "https://accounts.google.com/o/oauth2/auth?response_type=code&redirect_uri=" + googleplus_redirect_url + "&scope=https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile&client_id=" + googleplus_client_id;
Session["loginWith"] = "google";
return Redirect(Googleurl);
}
The credentials window (popup) is how you ask the user if you can access their data. There is no way to get access to a users data without asking the user first if you may access their data. That is how Oauth2 works.
If you are accessing your own data then you can use something called a Service account. Service accounts are pre authorized. You can take the service account and grant it access to your google calendar, you could give it access to a folder in Google drive. Then you can authenticate using the service account. Service accounts are like dummy users.
My article about service accounts: Google Developer service account
I have the following code and which works, but now the next step.
How and where do i have to set a session so the script "sees" that the user is already logged in?
if ($form->isValid()) {
$securePass = $this->getUsersTable()->getUserByUsername( $this->params()->fromPost('username') );
if( $securePass ){
$bcrypt = new Bcrypt();
if ($bcrypt->verify( $this->params()->fromPost('password') , $securePass->password)) {
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$authAdapter = new AuthAdapter(
$dbAdapter,
'users',
'username',
'password'
);
$authAdapter
->setIdentity($securePass->username)
->setCredential($securePass->password);
$result = $authAdapter->authenticate($authAdapter);
echo $result->getIdentity() . "\n\n";
}
else {
}
The Zend way of doing it is to use Authentication component that handles this for you.
http://framework.zend.com/manual/current/en/modules/zend.authentication.intro.html
This will allow you to check if user is logged in (you will have to setup the authentication adapter first):
use Zend\Authentication\AuthenticationService;
// TODO set-up authentication adapter
$auth = new AuthenticationService()
$identity = $auth->getIdentity();
For accessing post data you should also leverage the framework instead of accessing $_POST directly. In your controller:
$this->params()->fromPost('username');
$this->params()->fromPost('password');
This will guide you through the whole process of adding authentication layer to your app:
https://zf2.readthedocs.org/en/latest/modules/zend.authentication.adapter.dbtable.html
Using the AuthenticationService provided by Zend, setting the user in the PHP session is automatically taken care of.
A good thing to understand the authentication mechanism would be to read, and code along with this introduction to authentication:
http://framework.zend.com/manual/current/en/modules/zend.authentication.intro.html#adapters
In a custom AuthenticationAdapter "setting the user in the session", or identity persistence, would be done by returning \Zend\Authentication\Result with the result of the authentication and the user identity in the authenticate() method.
$user = $this->userService->findByEmail($this->email);
if($user !== false) {
if($this->encryption->verify($this->password, $user->getPassword()) {
return new Result(Result::SUCCESS, $user);
}
return new Result(Result::FAILURE, null);
}
$this->userService being the UserService that leads to the UserMapper
(more about Services: http://framework.zend.com/manual/current/en/in-depth-guide/services-and-servicemanager.html)
$user being the User entity with the encrypted password stored
$this->encryption being your encryption method (Zend\Crypt\Password\Bcrypt for example)
$this->email being the email/username provided by the form
$this->password being the password provided by the form
Result being Zend\Authentication\Result
This is an easy approach. More detailed Result types are:
/**
* General Failure
*/
const FAILURE = 0;
/**
* Failure due to identity not being found.
*/
const FAILURE_IDENTITY_NOT_FOUND = -1;
/**
* Failure due to identity being ambiguous.
*/
const FAILURE_IDENTITY_AMBIGUOUS = -2;
/**
* Failure due to invalid credential being supplied.
*/
const FAILURE_CREDENTIAL_INVALID = -3;
/**
* Failure due to uncategorized reasons.
*/
const FAILURE_UNCATEGORIZED = -4;
/**
* Authentication success.
*/
const SUCCESS = 1;
LoginController.php
if ($form->isValid()) {
$securePass = $this->getUsersTable()->getUserByUsername( $this->params()->fromPost('username') );
if( $securePass ){
$bcrypt = new Bcrypt();
if ($bcrypt->verify( $this->params()->fromPost( 'password' ) , $securePass->password ) ) {
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$authAdapter = new AuthAdapter(
$dbAdapter,
'users',
'username',
'password'
);
$authAdapter->setIdentity($securePass->username)
->setCredential($securePass->password);
$result = $authAdapter->authenticate($authAdapter);
$sesssionData = $authAdapter->getResultRowObject();
$auth = new AuthenticationService();
$storage = $auth->getStorage();
$storage->write($sesssionData);
return $this->redirect()->toRoute('user_list');
}
}
}
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$app = $e->getParam('application');
$app->getEventManager()->attach('render', array($this, 'setLayoutTitle'));
$eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'checkLogin'));
}
public function checkLogin(MvcEvent $e)
{
$iden = new AuthenticationService();
if( $iden->getIdentity() === NULL ){
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
$getController = explode( '\\', $controller );
if( isset( $getController[2] ) && $getController[2] != 'Login' ){
$controller = $e->getTarget();
return $controller->plugin('redirect')->toRoute('login');
}
}
}
Hi hope somone can help with this one. Ive had a birthday reminder app built, that aquires the usual permissions including offline access etc.
The app requires a daily cron job to be run on my server.
When I run the cron file a recieve the below error
Fatal error: Uncaught OAuthException: Invalid OAuth access token signature. thrown in blah/base_facebook.php on line 1140;
Is there a common reason for the error, am i doing anything wrong that stands out, and should i be displaying more code to get help from people?
below are the lines leading up to the error. My code ends on line 1140;
<?php
$name = 'api';
if (isset($READ_ONLY_CALLS[strtolower($method)])) {
$name = 'api_read';
} else if (strtolower($method) == 'video.upload') {
$name = 'api_video';
}
return self::getUrl($name, 'restserver.php');
}
protected function getUrl($name, $path='', $params=array())
{
$url = self::$DOMAIN_MAP[$name];
if ($path) {
if ($path[0] === '/') {
$path = substr($path, 1);
}
$url .= $path;
}
if ($params) {
$url .= '?' . http_build_query($params, null, '&');
}
return $url;
}
protected function getCurrentUrl() {
if (isset($_SERVER['HTTPS']) &&
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$protocol = 'https://';
}
else {
$protocol = 'http://';
}
$currentUrl = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$parts = parse_url($currentUrl);
$query = '';
if (!empty($parts['query'])) {
// drop known fb params
$params = explode('&', $parts['query']);
$retained_params = array();
foreach ($params as $param) {
if ($this->shouldRetainParam($param)) {
$retained_params[] = $param;
}
}
if (!empty($retained_params)) {
$query = '?'.implode($retained_params, '&');
}
}
// use port if non default
$port =
isset($parts['port']) &&
(($protocol === 'http://' && $parts['port'] !== 80) ||
($protocol === 'https://' && $parts['port'] !== 443))
? ':' . $parts['port'] : '';
// rebuild
return $protocol . $parts['host'] . $port . $parts['path'] . $query;
}
protected function shouldRetainParam($param) {
foreach (self::$DROP_QUERY_PARAMS as $drop_query_param) {
if (strpos($param, $drop_query_param.'=') === 0) {
return false;
}
}
return true;
}
protected function throwAPIException($result) {
$e = new FacebookApiException($result);
?>
CRON.php
<?php
require_once("src/facebook.php");
include("custom.php");
set_time_limit(0);
$config = array();
$config['array'] = $appID;
$config['secret'] = $appSecret;
$facebook = new Facebook($config);
$day = abs(date("j"));
$month = abs(date("n"));
$result = mysql_query("SELECT uid, uid2, name2 FROM birthdays WHERE birthmonth = '$month' AND birthday = '$day'");
while(($row = mysql_fetch_assoc($result)) && mysql_num_rows($result)) {
$link = $hostURL.'post.php?uid='.$row['uid'].'&uid2='.$row['uid2'];
$facebook->api('/'.$row['uid'].'/feed', 'POST',
array(
'link' => $link,
'from' => '299185790135651',
'picture' => $hostURL.'image.php?id='.$row['uid2'],
'name' => 'Send Cake',
'message' => 'It\'s '.$row['name2'].'\'s birthday today! Send them a virtual cake!',
'caption' => 'Sponsored by Intercake Ltd'
));
}
?>
also... what is 'from' => '299185790135651', ?
want to check my developer has put the right number here. Thanks
The best way to handle this is to use a try...catch statement. As follows:
try {
// some code that calls Facebook
} catch ( Exception $e ) {
// $e will contain the error - do what you want with it here
// e.g. log it or send an email alert etc.
}
The 'from' => '299185790135651' is a User / Page ID that publishes the message to the Feed. In this case, it's pointing to a Test Facebook Page.