I've an existing project that is built on :
AngularJS
Google App Engine backend, PHP, with an authentication based on JWT
I'm in process of rewriting the frontend to move to Angular 8 and I want to leverage the firebase features.
I'm currently working on integrating the authentication feature (username/password, google, twitter, facebook etc...)
And I'm thinking about my next step :
Once my use is authenticated with firebase, how can my GAE PHP backend check that the user is authenticated ?
In my JWT, I've set some basic user information, that are essential to my backend function.
uid, first name, last name, entityId, entityName, roleId, environmentId
I was imagining something like :
once authenticated with firebase, call my GAE Backend with the OAuth2 token
call some magic function that will validate the OAuth2 token and associate the firebase user, with my internal user table
reply with a JWT
Include the JWT and the OAuth2 token in every call
Would this work ? any suggestions ?
So here is how I did it :
On the client side (Angular Application), I use ngx-auth-firebaseui, to display the login form.
On the form, I set the call back that handle an authentication success:
login.component.html
<ngx-auth-firebaseui (onSuccess)="successfulLogin($event)"
(onError)="printError($event)">
</ngx-auth-firebaseui>
The code of the callback is here.
From the Firebase User object, I call the method getIdTokenResult() to get the firebase JWT.
And I then call my php backend via the authenticationService
login.component.ts
successfulLogin(user:User) {
console.log(user);
user.getIdTokenResult().then((idTokenResult:IdTokenResult)=> {
console.log(idTokenResult.token);
let token : string = idTokenResult.token;
let rcqJWTToken = this.authenticationService.authenticate( { token } as FirebaseJWT);
rcqJWTToken.subscribe((rcqToken:string)=> console.log("RCQ JWT Token : '"+rcqToken+"'"));
this.router.navigate['/welcome'];
});
}
Here I transmit the Firebase JWT to my php backend
authentication.service.ts
authenticate(firebaseJWTToken:FirebaseJWT):Observable<String>{
return this.http.post<String>(this.authenticationURL, firebaseJWTToken, httpOptions)
.pipe(
tap(_ => console.log('fetched RCQ JWT')),
catchError(this.handleError<String>('authenticate', ""))
);
}
On the server side :
I set the GOOGLE_APPLICATION_CREDENTIALS as an env var, like it is when deployed on Google App Engine
putenv("GOOGLE_APPLICATION_CREDENTIALS=/Users/myuser/.cred/GCP-project-ID.json");
I use Slimframework, so I instanciate the Firebase object in my dependencies.php file.
With the env var, Firebase do not need anything else.
check here : https://firebase-php.readthedocs.io/en/4.32.0/setup.html
use Kreait\Firebase;
use Kreait\Firebase\Factory;
/**
* Used to authenticate a firebase user, from it's Firebase JWT
* #property Firebase $firebase
* #param \Slim\Container $c
* #return Firebase
*/
$container['firebase'] = function (\Slim\Container $c)
{
$firebase = (new Factory)->create();
return $firebase;
};
and here comes the route where the authentication is done :
$app->post(getPrefix().'/firebase-authenticate', function($request, $response, $args) use ($app)
{
$token = $this->clientInputValidator->validateString("token" , $request->getParsedBodyParam("token" ), 1500 , true );
$username = "";
Logger::dataForLogging(new LoggingEntity(null, ["username"=>$username]));
try
{
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
}
catch (InvalidToken $e)
{
$response401 = $response->withStatus(401);
$response401->getBody()->write(json_encode(["error" =>"Authentication error"]));
$this->logger->error("Firebase authentication error", array('username' => $username, 'token' => $token));
return $response401;
}
$uid = $verifiedIdToken->getClaim('sub');
$user = $this->firebase->getAuth()->getUser($uid);
$this->logger->debug("Firebase JWT checked successfully", array('uid' => $uid,'user' => $user));
});
The main thing is here :
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
And the user details are retrieved here:
$user = $this->firebase->getAuth()->getUser($uid);
I can get the uid, email, and all the info in the Firebase JWT.
the token itself has a TTL of 1 hour, so I'll probably have to refresh the token and revalidate it against my backend.
Related
I had to install shopify-cli for develop shopify app with laravel. installation and test app are created successfully but when am i calling the API of shopify in Laravel app i got this error.
I am check header but no authorisation token pass. So my question how to get authenticate token get in Laravel starter app and call API of Shopify and i was follow PHP guide REST Admin API reference but without session i can not access shopify REST Admin API reference.
my code show like this...
Route::get('/rest-example', function (Request $request) {
/** #var AuthSession */
// $session = $request->get('shopifySession'); // Provided by the shopify.auth middleware, guaranteed to be active
$session = OAuth::callback(
$request->cookie(),
$request->query(),
['App\Lib\CookieHandler', 'saveShopifyCookie'],
);
$client = new Rest($session->getShop(), $session->getAccessToken());
$result = $client->get('products', [], ['limit' => 5]);
return response($result->getDecodedBody());
})->middleware('shopify.auth:online')->name('rest-api');
I think you want to create Custom App (not embedded) for your store.
You can read here about difference. I spent the whole day searching for solutions until get the idea.
All you need to do is to create a Custom App in your store, then get Admin API access token with you can use for REST API calls.
Here is my small example how I get it.
use Shopify\Clients\Rest;
Route::get('/test', function(Request $request) {
$client = new Rest(
env('SHOPIFY_APP_HOST_NAME'),
env('SHOPIFY_APP_ADMIN_ACCESS_TOKEN') // shpat_***
);
dd($client->get('products')->getDecodedBody());
});
I'm using Hybridauth 3 in my PHP app to make some periodical tweets on behalf of my account.
The app has all possible permissions. I'm giving it all permissions when it asks for them on the first auth step.
After that Twitter redirects me to the specified callback URL and there I'm getting a pair of access_token and access_token_secret.
But when I'm trying to make a tweet using these tokens - it gives me:
{"errors":[{"code":220,"message":"Your credentials do not allow access to this resource."}]}
Here's how I'm trying to make a tweet:
$config = [
'authentication_parameters' => [
//Location where to redirect users once they authenticate
'callback' => 'https://mysite/twittercallback/',
//Twitter application credentials
'keys' => [
'key' => 'xxx',
'secret' => 'yyy'
],
'authorize' => true
]
];
$adapter = new Hybridauth\Provider\Twitter($config['authentication_parameters']);
//Attempt to authenticate the user
$adapter->setAccessToken(/*tokens I've got from getAccessToken() on /twittercallback/*/);
if(! $adapter->isConnected()) {
// never goes here, so adapter is connected
return null;
}
try{
$response = $adapter->setUserStatus('Hello world!');
}
catch (\Exception $e) {
// here I've got the error
echo $e->getMessage();
return;
}
Tried to recreate tokens and key\secret pairs and passed auth process for the app many times, including entering password for my Twitter account (as suggested in some posts on stackoverflow) but still have this error.
P.S. According to this, Hybridauth has fixed the issue in the recent release.
It looks like you are using application authentication as opposed to user authentication. In order to post a tweet, you must authenticate as a user. Also, make sure your Twitter app has read/write privileges.
After comparing headers of outgoing requests from my server with the ones required by Twitter, I've noticed that Hybris doesn't add very important part of the header: oauth_token. At least it's not doing this in the code for Twitter adapter and for the scenario when you apply access token with setAccessToken(). It's just storing tokens in the inner storage but not initializing corresponding class member called consumerToken in OAuth1 class.
So to initialize the consumer token properly I've overridden the apiRequest method for Twitter class (before it used the defalut parent implementation) and added a small condition, so when consumer token is empty before the request - we need to try to init it.
public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [])
{
if(empty($this->consumerToken)) {
$this->initialize();
}
return parent::apiRequest($url, $method, $parameters, $headers);
}
I'm not sure that I've fixed it the best way, but as long as it's working - that's fine.
For your info setAccessToken was fixed in v3.0.0-beta.2 (see PR https://github.com/hybridauth/hybridauth/pull/880)
I faced the same error when implementing a sample app in clojure and the following resource was a huge help to sort out my confusion about application-only auth vs user authentication: https://developer.twitter.com/en/docs/basics/authentication/overview/oauth
I want to access the locations associated with my account and their reviews, for that I am using the google my business API and I have access to it (it does work on oAuthplayground).
Now I want to access the google my business api without logging into my account, for that I am trying to make it work with the service account. But no luck so far, please advice how to proceed with this. I have enabled the G suite in the service account and I have also tried to give access to the service account email (ID) for the my business manage but it stays in Invited state, as there is no way to actually accept the invite.
When I try to send request using my account as subject.
$client = new Google_Client();
$client->addScope('https://www.googleapis.com/auth/plus.business.manage');
$client->setAuthConfig(dirname(__FILE__) . '/Xyz Review API-service account.json');
$client->setSubject('xyz*****abc#gmail.com');
$business_service_class = new Google_Service_Mybusiness($client);
$result_accounts = $business_service_class->accounts->listAccounts();
echo json_encode($result_accounts);
exit;
Response:
{"nextPageToken":null}
If I use the google service account ID as email id in subject then I get following response.
$client->setSubject('xyz-review-service#xyz-review-api.iam.gserviceaccount.com');
Response:
Error 500
{ "error": "unauthorized_client", "error_description": "Unauthorized client or scope in request." }
If I am doing this completely wrong then please do advice how to proceed with this. Thank you.
Here is what I got working in 2021 with NodeJS:
To log in as a service account for server to server authentication you need to enable domain wide delegation for your service account. https://developers.google.com/admin-sdk/directory/v1/guides/delegation
Once you do this, you can make your service account log into the Google My Business API by impersonating the email address of an approved My Business manager. This is in NodeJS, here is what I used:
const { google } = require('googleapis'); // MAKE SURE TO USE GOOGLE API
const { default: axios } = require('axios'); //using this for api calls
const key = require('./serviceaccount.json'); // reference to your service account
const scopes = 'https://www.googleapis.com/auth/business.manage'; // can be an array of scopes
const jwt = new google.auth.JWT({
email: key.client_email,
key: key.private_key,
scopes: scopes,
subject: `impersonated#email.com`
});
async function getAxios() {
const response = await jwt.authorize() // authorize key
let token = response.access_token // dereference token
console.log(response)
await axios.get('https://mybusiness.googleapis.com/v4/accounts', {
headers: {
Authorization: `Bearer ${token}`
} // make request
})
.then((res) => { // handle response
console.log(res.data);
})
.catch((err) => { // handle error
console.log(err.response.data);
})
}
await getAxios(); // call the function
I faced the problem of authentication for my internal service with google apis.
Basically exists two method:
create the page to accept your application to access the google account
create a certificate to authenticate the application with "implicit" approval
as i said i'm using the google api for an internal project, so the first option is out of question (the service is not public).
Go to https://console.cloud.google.com and create a new project then go to "api manager" then "credentials" then create a "service credential".
If you follow all those steps you have a certificate with .p12 extension, it's your key to access to google api (remember you have to enable the key to access the specific google api you want).
I paste an example extracted from my project, i'm using google calendar, but the authentication is the same for each service.
$client_email = 'xxxx#developer.gserviceaccount.com';
$private_key = file_get_contents(__DIR__ . '/../Resources/config/xxxx.p12');
$scopes = array('https://www.googleapis.com/auth/calendar');
$credentials = new \Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key
);
$this->client = new \Google_Client();
$this->client->setAssertionCredentials($credentials);
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..
In my iOS app I am using TokBox to implement a private video chat feature.
I need to create a random session ID for each private chat, using the Server SDK API and I am having hard time understanding how to do it.
I successfully completed all the steps listed in the documentation “learning-opentok-php” available on the opentok github page.
I linked my OpenTok app with Heroku and configured the “web/index.php” to show me the static “SESSION_ID” and “TOKEN_ID” on my browser.
As explained in the documentation the “Sample App” uses a single session ID and does not generate a new session ID for each call. Please can someone provide some examples or advice on how to configure my app to generate each time a different Session ID?.
$app->get('/', function () use ($app) {
// If a sessionId has already been created, retrieve it from the cache
$sessionId = $app->cache->getOrCreate('sessionId', array(), function() use ($app) {
// If the sessionId hasn't been created, create it now and store it
$session = $app->opentok->createSession();
return $session->getSessionId();
});
// Generate a fresh token for this client
$token = $app->opentok->generateToken($sessionId);
$app->render('helloworld.php', array(
'apiKey' => $app->apiKey,
'sessionId' => $sessionId,
'token' => $token
));
});
try this code
send request on server get every time new sessionId.