Here is my code:
$client = new Google_Client();
$client->setApplicationName("dayPlanner");
$client->setClientId(env('googleClientID'));
$client->setClientSecret(env('googleClientSecret'));
$client->setAccessToken(session('token'));
if($client->isAccessTokenExpired()) {
$client->refreshToken($user->refreshToken);
$token = $client->getAccessToken();
session(['token' => $token]);
$client->setAccessToken($token); // necessary..?
}
This works, but I don't fully understand it. Most examples online show the refreshToken (and getAccessToken for the session data), but none I've seen then use setAccessToken again. Logic dictates this is necessary, and it works, but I'd like a definitive answer and to understand this a little better.
(Note, Laravel syntax used for setting/retriving session data)
Reading the PHP API code should answer your question.. See the code
If you look at the getAccessToken code, it just returns "token" that was already there.
public function getAccessToken()
{
return $this->token;
}
So doing a setAccessToken to set a token that was received using getAccessToken doesn't make much sense.
During the refreshToken setting time (see the code), it'll update the accessToken if there is none.
Related
I'm using composer to install the slim-skeleton. Those built in routes work as expected. I understand how to add in my previous routes and database connections, but I've been struggling on how to add in any JWT library. I've searched and searched but I'm not finding much documentation for Slim-4 and what I've tried always seems to fail one way or another.
So for example I use composer to install tuupola/slim-jwt-auth and it says to add the following code:
$app = new Slim\App;
$app->add(new Tuupola\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub"
]));
but where or how exactly do I add it to the middleware? Does it need to be added to app/middleware.php? All the documentation I read has a completely different file structure with other directories and whatnot. Once this is placed in the correct spot it looks like when a request is made without a token I should get a 401 Unauthorized response.
After that part is working I know I need to create a route to get my access token, but I'm not seeing anything about that in this library so I would assume I need another library to encode my token and return it from my request.
Once I actually get a token response and pass it in the headers for my actual request route I would assume I do something like the following
$app->get("/protected-route-name", function ($request, $response, $arguments) {
$token = $request->getAttribute("token");
// Not sure what to put next to verify the token and allow the response or display a error if there is no token or the token in invalid.
});
I'm open to firebase or any JWT library if someone has one they like and that works well, I just need some direction as I feel all the documentation is lacking.
use \Firebase\JWT\JWT;
get token
$headers = apache_request_headers();
if(isset($headers['Authorization'])){
$decoded = JWT::decode($headers['Authorization'], $publicKey, array("RS256"));
.... verify token.
}
$jwt = JWT::encode($payload, $privateKey, "RS256");
boom done.
you don't even really need to use middle ware to do this.
slim made itself way overly complex with that.
But the truth is between slim3 and slim 4, on a very basic setup, the only thing that has changed is the getBody() on the json writing.
honestly, not really sure how useful this is anymore to be honest. everything is cloudbased now. Only reason I found this is trying to figure out how to use Google Identity Platform with Slim.
How to connect to an IAP protected service with PHP via Service Account? I already fail to get the Authentication Bearer, so I guess I'm completely on the wrong track:
<?php
require_once 'vendor/autoload.php';
$scopes = ['https://www.googleapis.com/auth/iam'];
$client = new Google_Client;
$client->useApplicationDefaultCredentials();
$client->setScopes($scopes);
$client->setSubject('example#example.iam.gserviceaccount.com');
$client->setOpenidRealm('https://example.com');
$access_token = $client->getAccessToken();
var_dump($access_token);
Any pointer to where and how to start would be highly appreciated.
Have you seen https://cloud.google.com/iap/docs/authentication-howto ? Unfortunately we don't have sample code for PHP yet, but that explains the basic concepts. The "Robot Parade" section of https://cloudplatform.googleblog.com/2017/04/Getting-started-with-Cloud-Identity-Aware-Proxy.html may be helpful as well.
Unfortunately, IAP doesn't support access tokens for authentication, so you're going to need to get a service-account-signed JWT, and it needs to have a special "target_audience" claim. Is $client->config['signing_key'] set? If so, you have access to the service account's private key, and can... take a look at https://github.com/google/google-auth-library-php/blob/master/src/OAuth2.php#L417 and if you can get the credentials object you can do something like that, add the target_audience claim.
If you don't have access to the private key, which I think is only the case if you're running on GCE and using a service account from the metadata server, you'll have to use the IAM signBlob API: https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob . This requires some tricky setup on the GCE instance, https://cloud.google.com/iap/docs/authentication-howto documents it.
Sorry, I know this is all more complicated than it should be! I'm not a PHP expert, but I hope this at least helps.
-- Matthew, Identity-Aware Proxy engineer
I am trying to create a web-app with a simple dashboard with Analytics data for the accounts who logged in with Google. I am using Laravel with Socialite package, and I can log the users in with Google currently. I have my developer client-key and client-secret. I set scopes for Analytics read-only, and offline access, and I store customer name, email, Google ID, access token and refresh token in my database. I can log the user in without any problem.
What I want to do is for now, just access the profiles an Analytics account currently has. I followed Analytics API documentation examples but could not get it working. Since I am storing an access token and a refresh token, I think I should be able to authenticate the current user and get their Analytics data, but I could not find any simple methods from the Client and Analytics libraries. I will need to access their Analytics data offline, and this is why I think I should be able to authorize my requests with access token and refresh token, but I do not get any Analytics specific data from user login process. I am completely lost now, how do I authorize my requests to Anayltics API? I have been using AdWords API for more than 8 months, and everything is crystal clear in AdWords API documentation, but I could not get anything working with Analytics API.
These are my user login methods:
public function redirectToProvider()
{
$parameters = ['access_type' => 'offline'];
return Socialite::driver('google')
->scopes(['https://www.googleapis.com/auth/analytics.readonly'])
->with($parameters)
->redirect();
}
/**
* Obtain the user information from Google.
*
* #return Response
*/
public function handleProviderCallback()
{
$outsiderLogin = Socialite::driver('google')->stateless()->user();
$user = User::where('googleID', $outsiderLogin->id)->first();
// Register the user if there is no user with that id.
if (!$user) {
$user = new User;
$user->name = $outsiderLogin->name;
$user->googleID = $outsiderLogin->id;
$user->email = $outsiderLogin->email;
$user->token = $outsiderLogin->token;
$user->refreshToken = $outsiderLogin->refreshToken;
$user->save();
}
// Log the user in.
Auth::login($user);
return redirect('/home');
}
Thank you very much.
I have found the solution for now. At first, I figured that I needed the code that returns with authentication URL from Google, and when I inspect the Socialite package, I have found a protected method getCode() in \vendor\laravel\socialite\src\Two\AbstractProvider.php, which returns the code from the URL. I edited the source file of the package and changed the method type from protected to public, and that made it possible to use that method outside of the class, which allowed me to access the code from the URL, then store it in DB for further authentication requirements. But there were issues with this setup, first of all, I should find a way to keep that package without any update, since any update will rollback the changes I made to the source file. The second problem I faced was the way I store tokens. By default, Google Client API returns an array which contains the fields access_token, refresh_token, expires_in, id and created, and with these fields, it authenticates the requests to Analytics server. In my scenario, there were no standard array returning from the basic Socialite login process. There were access_token, refresh_token and expires variables and I stored them all in my database as well. This caused an issue with Google library, it asked for a structured array and I did not even have the variables expires_in and created, this is why I setup a fake array which tells Google to refresh token with every request, and this was not a good practice either.
At the end, I could not understand how to use any package online and I wrote my own simple authentication, and I do not know if it has any vulnerabilities, but it works for me, it may also work for those who needs it.
Here are my routes lines:
Route::get('auth/google', [
'as' => 'googleLogin',
'uses' => 'Auth\AuthController#redirectToProvider'
]);
Route::get('auth/google/callback', [
'as' => 'googleLoginCallback',
'uses' => 'Auth\AuthController#handleProviderCallback'
]);
And these are the AuthController methods:
/**
* Redirect the user to the Google authentication
*/
public function redirectToProvider()
{
// Create the client object and set the authorization configuration from JSON file.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/auth/google/callback');
$client->addScope(Google_Service_Analytics::ANALYTICS_READONLY);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType("offline");
$auth_url = $client->createAuthUrl();
return redirect($auth_url);
}
/**
* Obtain the user information from Google.
*
* #return redirect to the app.
*/
public function handleProviderCallback()
{
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
return redirect('auth/google');
} else {
// Authenticate the client, and get required informations.
$client = new Google_Client();
$client->setAuthConfig('/home/vagrant/Analytics/client_secret.json');
$client->authenticate($_GET['code']);
// Store the tokens in the session.
Session::put('token', $client->getAccessToken());
$service = new Google_Service_Oauth2($client);
$userInfo = $service->userinfo->get();
$user = User::where('googleID', $userInfo->id)->first();
// If no match, register the user.
if(!$user) {
$user = new User;
$user->name = $userInfo->name;
$user->googleID = $userInfo->id;
$user->email = $userInfo->email;
$user->refreshToken = $client->getRefreshToken();
$user->code = $_GET['code'];
$user->save();
}
Auth::login($user);
return redirect('/home');
}
}
I have placed the client_secret.json file I have downloaded from Google API Console into the specified folder, this may be different for you. I have also modified the migration file in order to match the required segemnts. After these steps, I am able treat that user as it is a simple user that registered with the basic Laravel auth.
Now I can query, say, the accounts in the user's Google Analytics account like this:
/**
* #var $client to be authorized by Google.
*/
private $client;
/**
* #var $analytics Analytics object to be used.
*/
private $analytics;
public function __construct()
{
$this->client = $this->AuthenticateCurrentClient();
$this->analytics = new Google_Service_Analytics($this->client);
}
private function AuthenticateCurrentClient(){
$user = Auth::user();
$token = Session::get('token');
// Authenticate the client.
$client = new Google_Client();
$client->setAccessToken($token);
$client->authenticate($user->code);
return $client;
}
public function GetAccounts(){
try {
$accountsObject = $this->analytics->management_accounts->listManagementAccounts();
$accounts = $accountsObject->getItems();
return $accounts;
} catch (apiServiceException $e) {
print 'There was an Analytics API service error '
. $e->getCode() . ':' . $e->getMessage();
} catch (apiException $e) {
print 'There was a general API error '
. $e->getCode() . ':' . $e->getMessage();
}
}
There were thousands of times Stack Overflow has helped me, I hope this helps someone to get things working.
You're not really going to find what you're looking for with the Socialite package shipped with Laravel (which is more used for logins and that's about it).
You can however find many Google Analytic packages (along with many other Laravel bundles) here which should help you make API calls:
http://packalyst.com/s/google%20analytics
More specifically, this package: https://github.com/spatie/laravel-analytics
That, or run your own Guzzle and cURL scripts. I use Guzzle when I need something quick without building a full blown API.
However, there's an interesting post here about using Socialite to access GA data. But you're quite limited. If you're creating user driven dashboards, I'd opt for a separate package.
https://laracasts.com/discuss/channels/tips/how-i-made-google-analytics-work-with-socialite
I'am also trying to do the same thing. By far, I've user authentication at place with oAuth 2.0 and Socialite package. I need the list of sites to be fetched from GA. And am totally stuck there. It would really be great if you can guide me as on how should I move on further..
According to the documentation the endpoint looks like
GET /debug_token?
input_token={input-token}&
access_token={access-token}
where
input_token: the access token you want to get information about
access_token: your app access token or a valid user access token from a developer of the app
Assuming I don't have a "valid user access token from a developer of the app" - just because I don't want to refresh it every 2 months and keep always it in mind - how would I perform it using "app access token"?
The getApplicationAccessToken() method is protected, so there is no way to access it without overriding it to public.
Any elegant solution that I'm missing?
PS: a call example that would fail with "You must provide an app access token or a user access token that is an owner or developer of the app" error due to lack of access token:
$tokenDebug = $fb->api('debug_token', array(
'input_token' => $token,
));
PPS: the "interesting" thing is that the error from above would appear not for every $token but for some, and I cannot see any obvious distinction between tokens that fail and that succeed.
PPPS: $token is a user access token
PPPPS: Created a feature request for FB PHP SDK https://developers.facebook.com/bugs/637897982899835
PPPPPS: Probably it could be better to create a pull request instead, but it's 1:30am and I'm too tired for that
OK, so if one needs an app access token, app_id|app_secret (both values concatenated with a pipe symbol in the middle) always works.
The method getApplicationAccessToken seems to have been protected in the PHP SDK up to 3.2.2 – whereas getAppId and getAppSecret are already public in that version; so those could be the alternative to hard-coding id and secret in place.
The PHP SDK has the getOAuth2Client() client method, that returns a \Facebook\Authentication\OAuth2Client instance.
This has the debugToken($accessToken) method, that returns a \Facebook\Authentication\AccessTokenMetadata instance that contains data about the access token.
$appid = '123456789';
$appsecret = 'foobar';
$api = new Facebook(['app_id' => $appid, 'app_secret' => $appsecret]);
$oauth = $api->getOAuth2Client();
$meta = $oauth->debugToken($accessToken);
$meta->validateAppId($appid); // Will throw a FacebookSDKException if invalid
$meta->getIsValid(); // boolean
$meta->getExpiresAt(); // \DateTime|null
I'm trying to get an OAuth access token to import some data into the fusion table. I'm trying to use the Google API PHP client. I have created a service account for that purpose, and am using the code, mostly from the serviceAccount example:
function access_token()
{
$client = new Google_Client();
$client->setAuthClass ('Google_OAuth2');
// ^ Don't know if this line is required,
// ^ but it fails just as well without it.
$client->setApplicationName ('Mysite.dom.ain');
$client->setAssertionCredentials (new Google_AssertionCredentials
( 'MANY-NUMBERS-LETTERS-DASHES#developer.gserviceaccount.com',
array ('https://www.googleapis.com/auth/fusiontables'),
file_get_contents ('path/to/my/privatekey.p12') ));
$client->setClientId ('NUMBERS-LETTERS-DASHES.apps.googleusercontent.com');
$client->authenticate();
// ^ Also fails equally good with and without this line.
return $client->getAccessToken();
}
A little debug output shows that $client->authenticate() returns true, but $client->getAcessToken() returns null. No exceptions are thrown. I have the feeling I'm doing something fundamentally wrong. If so, please forgive my stupidity and point me in the right direction.
You don't need the authenticate() call, but you'll need to call refreshTokenWithAssertion() to refresh the underlying access token. If you are using the client library to make signed requests, it will lazily make this call for you if underlying access token has expired.
The API requests to refresh the access_token are expensive, and have a low quota, so you'll want to cache the access_token.
// Set your client id, service account name, and the path to your private key.
// For more information about obtaining these keys, visit:
// https://developers.google.com/console/help/#service_accounts
const CLIENT_ID = 'INSERT_YOUR_CLIENT_ID';
const SERVICE_ACCOUNT_NAME = 'INSERT_YOUR_SERVICE_ACCOUNT_NAME';
// Make sure you keep your key.p12 file in a secure location, and isn't
// readable by others.
const KEY_FILE = '/super/secret/path/to/key.p12';
$client = new Google_Client();
$client->setApplicationName("Google FusionTable Sample");
// Set your cached access token. Remember to store the token in a real database instead of $_SESSION.
session_start();
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new Google_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/fusiontables'),
$key)
);
$client->setClientId(CLIENT_ID);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion();
}
// Get the json encoded access token.
$token = $client->getAccessToken();
I think all you did was correct, now you have two options left:
Use your $client to make a service call with something like that
$service = new Google_FusiontablesService($client);
$selectQuery = "select * from 1AwxQ46kfmPoYoq38e5CopJOWkCo_9GUU_ucD6zI";
$service->query->sql($selectQuery)
Or call the internal function refreshTokenWithAssertion() in order to get your token:
$client::$auth->refreshTokenWithAssertion();
$token = $client->getAccessToken(); //this should work now
For both cases I have examples in my GitHub Repo.