Fatal error: Call to a member function getAttributes() on array - php

I have been doing this google authentication tutorial to better understand how to use the google signin api and I have recently received this error:
Fatal error: Call to a member function getAttributes() on array.
It occurs whenever I am trying to:
$this->client->verifyIdToken()->getAttributes();
in the getPayload() function. I do not know why this is happening. My configuration is Windows 10 and I am using WAMP server to run this application. Any help would be appreciated.
<?php class GoogleAuth {
private $db;
private $client;
public function __construct(Google_Client $googleClient)
{
$this->client = $googleClient;
$this->client->setClientId('234sfsdfasdfasdf3223jgfhjghsdsdfge3.apps.googleusercontent.com');
$this->client->setClientSecret('fD5g4-B6e5dCDGASefsd-');
$this->client->setRedirectUri('http://localhost:9080/GoogleSigninTutorial/index.php');
$this->client->setScopes('email');
}
public function checkToken()
{
if(isset($_SESSION['access_token']) && !empty($_SESSION['access_token']))
{
$this->client->setAccessToken($_SESSION['access_token']);
}
else
{
return $this->client->createAuthUrl();
}
return '';
}
public function login()
{
if(isset($_GET['code']))
{
$this->client->authenticate($_GET['code']);
$_SESSION['access_token'] = $this->client->getAccessToken();
return true;
}
return false;
}
public function logout()
{
unset($_SESSION['access_token']);
}
public function getPayload()
{
return $this->client->verifyIdToken()->getAttributes();
}
}
?>

I've had the same problem.
From what I seemed to understand,
$attributes = $this->client->verifyIdToken()->getAttributes();
is an outdated way to access the array supposed to return the information of the google account (i.e. after running this line, $attributes was expected to be an array with all the information of the google account corresponding to the token.)
Try this instead
$this->client->verifyIdToken();
It seems that in the latest api (so far), this line in itself returns an array with the expected information (that's why you get an error when you add ->getAttributes(), because this function is not valid when called on an array.)
So simply run this line above to generate the array, and put it in echo if you want to see the values, like so
echo '<pre>', print_r($attributes), '</pre>';
If you don't see any array displayed, it may be that you have a
header('Location: url')
somewhere that is redirecting to another URL address right after executing this echo, thus it never shows. (Or a die)
You can also directly access specific attributes such as email, name, given_name, family_name by doing
$this->client->verifyIdToken()['email'];
$this->client->verifyIdToken()['name'];
//so on
Hope this can help.

Related

The GET method is not supported for this route. Supported methods: POST on Flutterwave implementation with laravel

I am implementing Flutterwave implementation as found on Medium but I am getting the error:
The GET method is not supported for this route. Supported methods: POST.
http://localhost:8000/rave/callback?resp=%7B%22name%22%3A%22opop%22%2C%22data%22%3A%7B%22data%22%3A%7B%22responsecode%22%3A%2200%22%2C%22responsetoken%22%3Anull%2C%22responsemessage%22%3A%22successful%22%7D%2C%22tx%22%3A%7B%22id%22%3A2424493%2C%22txRef%22%3A%22rave_611fc5fe12df9%22%2C%22orderRef%22%3A%22URF_1629472286526_3670035%22%2C%22flwRef%22%3A%22FLW-MOCK-44b7ecdb3a2183c971db03d669dc1554%22%2C%22redirectUrl%22%3A%22http%3A%2F%2Flocalhost%3A8000%2Frave%2Fcallback%22%2C%22device_fingerprint%22%3A%22888b449800a5003eaf1eeea02d5d52db%22%2C%22settlement_token%22%3_
I am implementing Post routes as shown in:
Route::post('/pay', 'RaveController#initialize')->name('pay');
Route::post('/rave/callback', 'RaveController#callback')->name('callback');
And on my controller, I've got:
public function initialize() {
//This initializes payment and redirects to the payment gateway
//The initialize method takes the parameter of the redirect URL
Rave::initialize(route('callback'));
}
/**
* Obtain Rave callback information
* #return void
*/
public function callback() {
$data = Rave::verifyTransaction(request()->txref);
dd($data); // view the data response
if ($data->status == 'success') {
//do something to your database
} else {
//return invalid payment
}
}
Please can anyone help me solve this problem? Especially since changing the route to get returns null. Thanks a whole lot!
that's because of you are calling the route again here that is mean you will go to this direction with get method
public function initialize(){
Rave::initialize(route('callback'));
}
the best solution to you you have to call this function without routing it as following
public function initialize(Request $request){
//here now you will not routing with get you will call the function inside it
Rave::initialize($this->callback($request->all()));
}
/**
* Obtain Rave callback information
* #return void
*/public function callback($request){
$data = Rave::verifyTransaction($request->txref);
dd($data); // view the data response
if ($data->status == 'success') {
//do something to your database
}
else {
//return invalid payment
}
}
I suppose Route::post('/rave/callback', 'RaveController#callback')->name('callback'); Is your problem. It should be Route::get
Hi. I am sorry for editing this directly as I cant find the reply button. Route::get() returns null for this particular implementation.

How to validate a parameter for a class within a method and prevent use if invalid?

I am building a small class to handle api requests and I am running into an issue with error handling (I am also a novice at OOP so please with me) I need to restrict or throw an error with any methods in my class that require the user parameter to be set and I also need to samething if token has not been retreived and I can't seem to wrap my head around it.
This is what I have so far...
$user array is set in a config file outside of class like so (empty by default):
$user = array(
'user_email' = '',
'user_pass' = ''
);
Class for handling API (simplified for question)
class eventAPI {
private $user
private $token
public function __construct($user) {
$this->user = $user;
// if possible assign token when instantiated
$this->retrieve_token($user);
}
private function retreive_token($user) {
// Check if user parameter has been set
if($this->validate_user_parameter()) {
// use credentials to make HTTP request for token
$token = 'somerandomtoken';
// assign token property retreived value
$this->token = $token;
} else {
echo 'User parameter has not been set.' // Not real message just for testing
return FALSE;
}
}
public function show_all_events() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// reduntant code... Can't wrap my head around another way for checking for token.
public function show_single_event() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// This is mostly where I am confused how to solve.
private function validate_user_parameter() {
foreach($this->user as $key => $value) {
// Remove whitespace from value
$value = trim($value);
if(empty($value)) {
echo 'User credentials have not been set'; // for testing purposes
return FALSE;
}
}
}
private function validate_token_retreived() {
$result = FALSE;
// Bool value not sure if this is the best way to do this
if(isset($this->$token)) {
$result = TRUE;
}
return $result;
}
}
First issue: I need to validate the user parameter which is in an array so I can use with a private method to retrieve the token. I chose to use a foreach loop to check each value but it seems a little archaic.
Second Issue: I have a redundant check in place for each public method to check if token is valid. Is there a better way to do this with OOP? I have many methods that require the token.
In short, how can I make sure that once the class is instantiated a public method that will be used by end user will not fire if any validation fails. The user info only needs to be valid once when instantiated and then the token needs to be valid for most remaining methods.
You don't need to pass $user parameter to retreive_token function. You got it in class scope. Just use $this->user in the function to access it. Also you didn't use it in that function, so why u passed it?
You didn't send true in any function.
There's nothing wrong with for-each but you want to check array_map too. Atleast you're applying a function to every item in array. It can be usefull. ps: seems for-each still faster then array_map
Also you would want to check empty function on which cases it returns false
You can use multiple returns in a function. You dont need to set a variable to do that.
Example
private function validate_token_retreived()
{
if(isset($this->token))
return true;
return false;
}
You couldn't use else in most cases.
Example
public function show_all_events()
{
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
// and return here
}
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE; // do you really return here? seems you are just generating an output
}

Laravel - return a redirectResponse selectively generated in a function

Part of my application is a multi-stage checkout process; during the latter pages of this I first run a sanity check on each request to verify the user actually has some items in their basket: if not they're sent back to the beginning.
I have a controller function like this which is called from multiple routes for DRY purposes.
private function checkBasketFull($request)
{
if (self::isBasketEmpty($request)) {
return redirect('/')->with('status', config('app.empty_basket_message'));
}
}
When I call it, I can't just do:
self::checkBasketFull($request);
because without a return the redirect doesn't fire, only the session data is sent.
And I can't do:
return self::checkBasketFull($request);
because that will give an error if there's no redirect or abort the method if checkBasketFull returns anything else.
My current (working) code is:
$check = self::checkBasketFull($request);
if ($check) {
return $check;
}
Is there an alternative way of writing this on a single line, or modifying the checkBasketFull function, so the redirect will occur if the basket is empty but execution will continue as normal if it isn't?
Either use this:
if ($redirect = self::checkBasketFull($request)) return $redirect;
Or throw an error and catch it in the global error handler.
However, instead of returning and checking that for a redirect like that, I'd much rather keep it as two completely separate methods:
public function someRoute(Request $request)
{
if ($this->isBasketEmpty($request)) return $this->redirectBasketEmpty();
// Continue processing this request...
}
protected function isBasketEmpty(request)
{
// run your login here...
}
protected function redirectBasketEmpty()
{
return redirect('/')->with('status', config('app.empty_basket_message'));
}
Feels cleaner to me.

When using CodeIgniter + Facebook PHP SDK: getUser() always returns 0

I am attempting to use the Facebook PHP SDK in conjunction with CodeIgniter to allow users to login to my site using Facebook Connect. No matter what I try, getUser() always returns 0, even after (apparently) successful authentication via Facebook.
CodeIgniter version: 2.1.3
Facebook PHP SDK version: 3.2.2
I have created a config file, facebook.php, in the application/config folder and I am loading the Facebook PHP SDK via CodeIgniter's $this->load->library(...) method. The library is indeed getting loaded and I can successfully call many of the get...() methods including getAccessToken(), getAppId() and getAppSecret(), all of which return their expected values.
Here is a stripped down version of my login controller: (note that I also provide an alternate method of logging in via email, hence the CodeIgniter session code sprinkled throughout)
class Login extends CI_Controller {
public function __construct()
{
//Call parent constructor
parent::__construct();
//Magic sauce - not sure if this is required but a lot of other people
//are recommending it to be included (happy to remove it if necessary)
parse_str($_SERVER['QUERY_STRING'], $_REQUEST);
//Load facebook library
$facebook_config = $this->load->config('facebook');
$this->load->library('facebook', $facebook_config);
}
public function index()
{
//Check if user is logged in
$user_id = $this->session->userdata('user_id');
$is_logged_in = $this->session->userdata('is_logged_in');
if(($is_logged_in) && ($user_id != 0)) {
//Logged in - redirect to game
redirect('game');
} else {
//Not logged in
//Get facebook login url
$facebook_data = array(
'redirect_uri' => 'hxxp://xxxxxxxx.com/facebook_login/',
'scope' => 'email'
);
$data['facebook_login_url'] = $this->facebook->getLoginUrl($facebook_data);
//Redirect to login form
$this->load->view('login/login_form', $data);
}
}
public function facebook_login()
{
//Always returns 0!! Even after authenticating via facebook!
$facebook_user_id = $this->facebook->getUser();
if ($facebook_user_id) {
try {
$user_profile = $this->facebook->api('/me');
print_r($user_profile);
} catch (FacebookApiException $e) {
echo $e->getMessage();
}
} else {
echo "Could not log in with Facebook";
}
}
}
The stripped down view (login_form.php) is as follows:
<html>
<head>
<title>Facebook Connect Test</title>
</head>
<body>
<a href='<? echo $facebook_login_url; ?>'>Login with Facebook</a>
</body>
</html>
I have a route that redirects hxxp://xxxxxxxx.com/facebook_login to the login/facebook_login method, which is working.
I am running this code on a live development server.
My current flow is as follows:
Load hxxp://xxxxxxxx.com/ (Routes to login controller, which loads login_form view)
Click "Login with Facebook" link
Facebook asks me to login (which I do)
Facebook asks me to give permission to my app (which I do)
Facebook redirects me to the url specified in the redirect_uri parameter, which is identical to the one on the app settings page
And here's where it all goes wrong. The $this->facebook->getUser() method ALWAYS returns 0, even after authentication.
I have been scouring the Facebook developer documentation and everywhere else on the internet I can think of trying to find an answer to this. I have come across many posts similar to this and have tried to apply the solutions suggested, but to no avail.
What am I doing wrong?
The getCode() method in base_facebook.php uses the $_REQUEST global to store data. PHP 5.3.0 and greater uses the "request_order" param in php.ini, and by default $_REQUEST does not contain Cookie variables.
Per php.net (http://php.net/manual/en/ini.core.php#ini.request-order):
"This directive describes the order in which PHP registers GET, POST and Cookie variables into the _REQUEST array. Registration is done from left to right, newer values override older values.
If this directive is not set, variables_order is used for $_REQUEST contents.
Note that the default distribution php.ini files does not contain the 'C' for cookies, due to security concerns."
So it looks like your options are to modify the getCode() method like Max Power did above, or update your php.ini and add the "C" value to the request_order setting.
I managed to solve my problem. The questions linked to by Qweick and Stéphane Bruckert had the solution. The problem lies in the getCode() function of the base_facebook.php file.
The getCode() function needs to be modified. The modifications I used are listed below.
Existing non-working code:
protected function getCode() {
if (isset($_REQUEST['code'])) {
if ($this->state !== null &&
isset($_REQUEST['state']) &&
$this->state === $_REQUEST['state']) {
// CSRF state has done its job, so clear it
$this->state = null;
$this->clearPersistentData('state');
return $_REQUEST['code'];
} else {
self::errorLog('CSRF state token does not match one provided.');
return false;
}
}
return false;
}
Modified working code:
protected function getCode() {
$server_info = array_merge($_GET, $_POST, $_COOKIE);
if (isset($server_info['code'])) {
if ($this->state !== null &&
isset($server_info['state']) &&
$this->state === $server_info['state']) {
// CSRF state has done its job, so clear it
$this->state = null;
$this->clearPersistentData('state');
return $server_info['code'];
} else {
self::errorLog('CSRF state token does not match one provided.');
return false;
}
}
return false;
}
The getUser() call now returns a valid user Id and the Facebook API calls now return valid data.
Thanks to everyone that helped point me in the right direction!

Laravel facebook-sdk bundle logout not working

I am using Facebook-sdk bundle for Laravel and everything works fine, except the logout link. When I click logout, I get redirected and all looks like it is working, but when it loads the page back, I'm still logged in?
Is this a Laravel problem maybe? Does it store sessions differently?
I've built this class, but as I said, I don't think this is a problem because all is working fine, except logout session is not getting cleared.
Code:
class Fb{
// -----------------------------------------------------------------------
// Variables
private $ioc; // IOC container
public $state; // If logged or not
public $data; // Data that came from request
public $settings = array("name,gender");
// -----------------------------------------------------------------------
// Logical functions
public function __construct(){
$this->ioc = IoC::resolve('facebook-sdk');
if ($this->getUser()) {
try {
$this->request();
$this->state = true;
} catch (FacebookApiException $e) {
error_log($e);
}
}else{
$this->state = false;
}
}
public function getUser(){
return $this->ioc->getUser();
}
public function request(){
$this->data = $this->ioc->api("/me?fields=".implode($this->settings));
}
public function debug(){
return dd($this->data);
}
// -----------------------------------------------------------------------
// Login & Logout links
public function login(){
return $this->ioc->getLoginUrl();
}
public function logout(){
return $this->ioc->getLogoutUrl();
}
// -----------------------------------------------------------------------
// Get data via SDK
// Name
public function name(){
return $this->data['name'];
}
// Picture
public function picture($w=50,$h=50){
return "https://graph.facebook.com/". $this->data['id'] ."/picture?width=$w&height=$h";
}
// Gender
public function gender(){
return $this->data['gender'];
}
}
Thanks for any help!
Cheers!
The underlying facebook php sdk uses the built in php sessions (by default) to store persistent information like the authenticated facebook user's id.
However the sdk won't destroy this information on its own since it's difficult to tell when that should happen automatically.
You can clear this persisted information with the destroySession method on the facebook sdk object. The best place to call this method is on the logout url's redirect back endpoint, since that is where the visitor gets directly after facebook done with it's own logout.
This would look like:
// method on Fb class
public function destroySession() {
// just forward the call down to the sdk object
$this->ioc->destroySession();
}
You probably want to set up a route where users will arrive after logout and pass it into getLogoutUrl() like this:
// method on Fb class
public function logout(){
// tell explicity where to send the user when facebook is done, otherwise the current url will be used
return $this->ioc->getLogoutUrl(array('next' => URL::to_route('after_logout')));
}
And have a route like this:
Route::get('after_logout', array('as' => 'after_logout', 'do' => function() {
$fb = new Fb();
// call the session clearing
$fb->destroySession();
// send the user to its merry way
return Redirect::to('/');
}));

Categories