BACKGROUND
We have a Wordpress site that requires a special kind of Facebook authentication. The WP site needs to authenticate a user with a different app, and then log the user into the app. The web app is built by us, and it has an API that the WP site uses to interact with it. Before we all get rowdy, this whole authentication process works using a normal login form. Using the API, we can easily see that the user is or is not logged into the app, and display that on the WP site.
PROBLEM
As stated, we need Facebook authentication. I am using Facebooks PHP SDK v4 and have it built into a custom plugin so as to keep the code seperate from the theme. When a user clicks on the FB icon, it shows the popup with the correct redirect URL. After a while, this popup closes, but there is nothing in the result. A few well placed var_dumps reveal that I'm not getting anything back from FacebookRedirectLoginHelper. This means I can't get the session which in turns means no user info.
CODE
As stated, I've created a plugin (ns-social.php) which handles everything. Here is that file:
/**
* Plugin Name: NS Social Plugin
*/
require_once 'ns-social-init.php';
require_once 'ns-callback-functions.php';
require_once 'ns-facebook.php' ;
$fb = null;
add_action('plugins_loaded','load_fb');
function load_fb() {
global $fb;
$fb = new WP_Facebook();
}
/**
* SOCIAL LOGIN CHECK
*/
function get_fb_url()
{
global $fb;
return $fb->login_url();
}
ns-social-init.php starts a session that FB can use:
/**
* Any functions that are required during initialisation of Wordpress or this plugin
*/
add_action('plugins_loaded', 'start_session');
function start_session()
{
if (!session_id()) {
session_start();
}
}
ns-callback-functions.php contains all the callback functions for the redirects. These are all shortcodes that are placed in pages, so the url will be www.site.com/facebook-callback and that page will only have [facebook-callback] in it which will handle the request.
add_shortcode('facebook_callback', 'facebook_callback');
function facebook_callback()
{
global $fb;
if (isset($_GET['error'])) {
if ($_GET['error'] == 'access_denied') {
echo "<script>
if(window.opener != null) {
window.close();
}
</script>";
exit;
}
}
$session = $fb->get_session();
$userArr = $fb->get_user();
$user['name'] = $userArr['first_name'];
$user['surname'] = $userArr['last_name'];
$user['email'] = $userArr['email'];
$user['verified'] = $userArr['verified'];
$_SESSION['registeruser'] = $user;
$_SESSION['registertype'] = 'Facebook';
$action = "";
die(var_dump($_SESSION,true));
if (user_exists($user['email'])) {
$action = '?login';
}
wp_redirect(home_url('social-register/' . $action));
}
And last but not least, my ns-facebook.php file:
use Facebook\FacebookRedirectLoginHelper;
use Facebook\FacebookSession;
use Facebook\FacebookRequest;
class WP_Facebook
{
var $helper;
var $session;
var $permissions;
var $loginurl;
public function __construct()
{
// Initialize the SDK
FacebookSession::setDefaultApplication('0appId145', '00hahaitsmysecret23523');
$this->permissions = ['public_profile', 'email'];
$this->helper = new FacebookRedirectLoginHelper(home_url('facebook-callback'));
$this->loginurl = $this->helper->getLoginUrl($this->permissions);
}
/**
* Returns the login URL.
*
* #return string
*/
public function login_url()
{
return $this->loginurl;
}
/**
* Returns the current user's info as an array.
*/
public function get_user($session = null)
{
if(empty($session)) $session = $this->session;
if($session) {
/**
* Retrieve User's Profile Information
*/
// Graph API to request user data
$request = new FacebookRequest($session, 'GET', '/me');
$response = $request->execute();
// Get response as an array
$user = $response->getGraphObject()->asArray();
return $user;
}
return false;
}
public function get_session() {
try {
$this->session = $this->helper->getSessionFromRedirect();
} catch(FacebookRequestException $ex) {
// When Facebook returns an error
} catch(\Exception $ex) {
// When validation fails or other local issues
}
if ($this->session) {
return $this->session;
}
}
}
WHAT HAVE I TRIED
I've gone through quite a few questions on SO. What I have noticed is, when I first run the page, my FBRLH_state in my session is, abcdef for example. But when I get a response after clicking the login button, my FBRLH_state is xyz. I don't know if this has an effect on the outcome. If it could, how would I use this state? I don't set it, I'm assuming that the FB SDK does.
TL;DR
FB PHP SDK v4 is not sending back anything when I use FacebookRedirectLoginHelper. Why would it do this, and how do I fix it?
did you make a test with just the offical FacebookRedirectLoginHelper from official GitHub?
I have used the Facebook SDK a couple of times and never had a problem with missing returns.
So I fixed my issue with help from corvuszero's comment.
Here's the code in my ns-facebook.php file:
use Facebook\FacebookSession;
use Facebook\FacebookRequest;
use Facebook\FacebookRedirectLoginHelper;
class WP_Facebook
{
var $helper;
var $session;
var $permissions;
var $loginurl;
public function __construct()
{
// Initialize the SDK
FacebookSession::setDefaultApplication('303664476506500', '0197b7f08cc46f051ddb92dfba077484');
$this->permissions = ['public_profile', 'email'];
$this->helper = new FacebookRedirectLoginHelper( home_url('facebook-callback') );
try {
$this->session = $this->helper->getSessionFromRedirect();
} catch (FacebookRequestException $e) {
// handler
} catch (Exception $e) {
// handler
}
if(isset($_SESSION['fb_token'])) {
$this->session = new FacebookSession( $_SESSION['fb_token'] );
}
if($this->session) {
$_SESSION['fb_token'] = $this->session->getToken();
} else {
$this->loginurl = $this->helper->getLoginUrl($this->permissions);
}
}
/**
* Returns the login URL.
*
* #return string
*/
public function login_url()
{
return $this->loginurl;
}
/**
* Returns the current user's info as an array.
*/
public function get_user()
{
if($this->session) {
/**
* Retrieve User’s Profile Information
*/
// Graph API to request user data
$request = new FacebookRequest($this->session, 'GET', '/me');
$response = $request->execute();
// Get response as an array
$user = $response->getGraphObject()->asArray();
return $user;
}
return false;
}
public function get_session() {
return $this->session;
}
}
Related
Introduction
For my personal project i am using
Symfony v4.2 with
XAMPP and
Widows 10 Pro
In order to not to display route parameters in URL i save them in the table.
Then in the controller i check if there is variable (that keeps UUID that corresponds to route parameters) in the session.
If i get no variable in session it should redirect to section start page, where UUID and initial data in the table are setup.
Redirect logic is extracted to helper service. In order to redirect to work there are copied functions redirectToRoute and redirect
I test this functionalit by deleting php session variables in temp folder and PHPSESSID cookie in the browser.
Problem
The prolem is - it does not redirect to secton start page.
I can see that correct if branch is selected, but then it "just stops" and does not execute redirect.
Code
public function checkWhereaboutsExist()
{
$em = $this->entityManager;
$repo_whereabouts = $em->getRepository(Whereabouts::class);
$whereabouts = $this->session->get('whereabouts');
if (($whereabouts === null) || ($whereabouts === ''))
{
$data = 'whereabouts === '.$whereabouts;
dump($data);
/*
HERE IT STOPS
*/
return $this->redirectToRoute('section_start');
}
else
{
$my_whereabouts = $repo_whereabouts->getWhereabouts($whereabouts);
if (!$my_whereabouts)
{
return $this->redirectToRoute('section_start');
}
}
}
Question
Does enyone have some ideas about what is the culprit in this case?
You could try to inject the router into your service class:
use Symfony\Component\Routing\RouterInterface;
class MyService
{
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function checkWhereaboutsExist()
{
// your code ...
return new RedirectResponse($this->router->generate('section_start'));
}
}
Hummmm, i suppose that your code is in a service and not in your controller ?
You can't redirect from a service but only from controller as controller send the final response.
You have to return a boolean from your service and redirect from your controller :
public function hasToGoToStart()
{
$em = $this->entityManager;
$repo_whereabouts = $em->getRepository(Whereabouts::class);
$whereabouts = $this->session->get('whereabouts');
if (($whereabouts === null) || ($whereabouts === ''))
{
return true;
}
else
{
$my_whereabouts = $repo_whereabouts->getWhereabouts($whereabouts);
if (!$my_whereabouts)
{
return true;
}
}
return false;
}
and in your controller :
if ($myService->hasToGoToStart()) {
// redirect
}
I was trying to made a post for google plus moment in php, i using below code, but when trying to post i am getting authorization error, I am doing with codeignitter
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
include_once 'base_controller.php';
class Gp_auth extends Base_controller {
private $_gp_client;
public function __construct() {
parent::__construct();
$this->load->library('googleplus');
$this->_gp_client = $this->googleplus->client;
}
public function index() {
if ($this->input->get_post('code')) {
try {
$this->_gp_client->authenticate($this->input->get_post('code'));
$access_token = $this->_gp_client->getAccessToken();
$this->session->set_userdata('access_token', $access_token);
redirect('/gp_auth/me');
} catch (Google_Auth_Exception $e) {
_print($e);
}
} else {
$this->_gp_client->addScope(array(
'https://www.googleapis.com/auth/plus.login',
'https://www.googleapis.com/auth/plus.moments.write'
));
$this->_gp_client->setRequestVisibleActions('http://schemas.google.com/AddActivity');
try {
echo anchor($this->_gp_client->createAuthUrl(), 'Conect Me');
} catch (Google_Auth_Exception $e) {
_print($e);
}
}
}
public function me() {
try {
$this->_gp_client->setAccessToken($this->session->userdata('access_token'));
$response = $this->googleplus->plus->people->get('me');
_print($response->id);
$post_data = array(
'gp_id' => $response->id,
'gp_access_token' => $this->session->userdata('access_token'),
'post_body' => 'Hello Google moment',
'post_attachment' => ''
);
$this->load->library('sns_post');
echo $this->sns_post->gp_post($post_data);
} catch (Google_Auth_Exception $e) {
_print($e);
}
}
}
index function was for authentication
and 'me' function for for post moment
and below code for library code of moment post , which is called from me function
public function gp_post($post_data) {
$this->_CI->load->library('googleplus');
_print($post_data['gp_access_token']);
$this->_CI->googleplus->client->setAccessToken($post_data['gp_access_token']);
$this->_CI->googleplus->client->setRequestVisibleActions('http://schemas.google.com/AddActivity');
$this->_CI->googleplus->item_scope->setId($post_data['gp_id']);
$this->_CI->googleplus->item_scope->setType("http://schema.google.com/AddAction");
$this->_CI->googleplus->item_scope->setName("The Google+ Platform");
$this->_CI->googleplus->item_scope->setDescription($post_data['post_body']);
if (!empty($post_data['post_attachment'])) {
$this->_CI->googleplus->item_scope->setImage($post_data['post_attachment']);
}
$this->_CI->googleplus->moment_body->setTarget($this->_CI->googleplus->item_scope);
// Execute the request
try {
$momentResult = $this->_CI->googleplus->plus->moments->insert('me', 'vault', $this->_CI->googleplus->moment_body);
_print($momentResult);
} catch (Google_Auth_Exception $e) {
_print($e);
} catch (Google_Service_Exception $servic_exception) {
_print($servic_exception);
}
if ($response->meta->status == 201) {
return $response->response->id;
} else {
return false;
}
I think you have get authorization error because of following reason -
Not setting email id & project name using developer console link APIs & Auth->Consent Screen.
You are calling deprecated API $this->_CI->googleplus->item_scope->setId($post_data['gp_id']);. To Call this you have to use this class new Google_Service_Plus_ItemScope();
$this->_CI->googleplus->moment_body->setTarget($this->_CI->googleplus->item_scope); .To call this method you have to use this class new Google_Service_Plus_Moment();
I have setup demo code in git for you. Google-Plus-API-Codeigniter-Starter. You can use same code to start with. I have provided detailed instruction in git repository too.
You have to Register your application using Google Console Settings as below:
Go to Google Developer Console
Create Project
To Get Google+ API Access go to: APIs & Auth->APIs -> enable Google+ API
To Get client_id & client_secret go to: APIs & Auth->Credentials->Create new Client ID
To Get api_key go to: APIs & Auth->Credentials->Create New Key->Browser key
To Get application_name go to: APIs & Auth->Consent Screen
Set Email Address
Product Name
Note: Ensure you have get all the required access from google as mentioned in point 5
Conclustion: You are using Latest google-api-php-client client. And trying to call method in an older way. Hence its 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('/');
}));
I've recently started using Zend Framework and I'm still pretty used to session_start, and assigning variables to certain session names (ie: $_SESSION['username'] == $username)
I'm trying to figure out how to do something similar to this in Zend. Right now, my auth script checks the credentials using LDAP against my AD server and, if successful, authenticates the user.
I want to create a script that will allow an admin user to easily "enter" someone else's session. Let's say admin1 had an active session and wanted to switch into user1's session. Normally I would just change the $_SESSION['username'] variable and effectively change the identity of the user logged in.
But with Zend, I'm not quite sure how to change the session info. For what it's worth, here's my authentication script:
class LoginController extends Zend_Controller_Action
{
public function getForm()
{
return new LoginForm(array(
'action' => '/login/process',
'method' => 'post',
));
}
public function getAuthAdapter(array $params)
{
$username = $params['username'];
$password = $params['password'];
$auth = Zend_Auth::getInstance();
require_once 'Zend/Config/Ini.php';
$config = new Zend_Config_Ini('../application/configs/application.ini', 'production');
$log_path = $config->ldap->log_path;
$options = $config->ldap->toArray();
unset($options['log_path']);
require_once 'Zend/Auth/Adapter/Ldap.php';
$adapter = new Zend_Auth_Adapter_Ldap($options, $username, $password);
$result = $auth->authenticate($adapter);
if ($log_path) {
$messages = $result->getMessages();
require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Log/Filter/Priority.php';
$logger = new Zend_Log();
$logger->addWriter(new Zend_Log_Writer_Stream($log_path));
$filter = new Zend_Log_Filter_Priority(Zend_Log::DEBUG);
$logger->addFilter($filter);
foreach ($messages as $i => $message) {
if ($i-- > 1) { // $messages[2] and up are log messages
$message = str_replace("\n", "\n ", $message);
$logger->log("Ldap: $i: $message", Zend_Log::DEBUG);
}
}
}
return $adapter;
}
public function preDispatch()
{
if (Zend_Auth::getInstance()->hasIdentity()) {
// If the user is logged in, we don't want to show the login form;
// however, the logout action should still be available
if ('logout' != $this->getRequest()->getActionName()) {
$this->_helper->redirector('index', 'index');
}
} else {
// If they aren't, they can't logout, so that action should
// redirect to the login form
if ('logout' == $this->getRequest()->getActionName()) {
$this->_helper->redirector('index');
}
}
}
public function indexAction()
{
$this->view->form = $this->getForm();
}
public function processAction()
{
$request = $this->getRequest();
// Check if we have a POST request
if (!$request->isPost()) {
return $this->_helper->redirector('index');
}
// Get our form and validate it
$form = $this->getForm();
if (!$form->isValid($request->getPost())) {
// Invalid entries
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
// Get our authentication adapter and check credentials
$adapter = $this->getAuthAdapter($form->getValues());
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
// Invalid credentials
$form->setDescription('Invalid credentials provided');
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
// We're authenticated! Redirect to the home page
$this->_helper->redirector('index', 'index');
}
public function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_helper->redirector('index'); // back to login page
}
}
Is there any way to do what I have described? Thanks for any suggestions.
Given your code, the result of authenticating is stored in the PHP session through a Zend_Auth_Storage_Session object.
Calling Zend_Auth::getIdentity() gets access to the storage and returns the result if it is not empty. Likewise, you can change the stored identity by getting access to the underlying storage and changing its value. The actual identity stored as a result of authenticating with Zend_Auth_Adapter_Ldap is just a string value representing the LDAP username.
To effectively change the logged in user, you can do:
Zend_Auth::getInstance()->getStorage()->write('newUserName');
This assumes the default behavior which should be in place given your code.
What I do in my applications after successful authentication is to create a new object of some User model, and write that to the Zend_Auth session so that I have more information about the user available in each session, so you should be aware that different things can be in the storage depending on the application.
This is what I do for example:
$auth = new Zend_Auth(...);
$authResult = $auth->authenticate();
if ($authResult->isValid() == true) {
$userobj = new Application_Model_UserSession();
// populate $userobj with much information about the user
$auth->getStorage()->write($userobj);
}
Now anywhere in my application I call Zend_Auth::getInstance()->getIdentity() I get back the Application_Model_UserSession object rather than a string; but I digress.
The information that should help you is:
$user = Zend_Auth::getInstance()->getIdentity(); // reads from auth->getStorage()
Zend_Auth::getInstance()->getStorage()->write($newUser);
I am using Zend_auth for authentication purposes.Code for the same is as follows:
$authAdapter = $this->getAuthAdapter();
$authAdapter->setIdentity($username)
->setCredential($password);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
# is the user a valid one?
if ($result->isValid()) {
# all info about this user from the login table
# ommit only the password, we don't need that
$userInfo = $authAdapter->getResultRowObject(null, 'password');
# the default storage is a session with namespace Zend_Auth
$authStorage = $auth->getStorage();
$authStorage->write($userInfo);
$emp_id = $userInfo->employee_id;
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
$array_db = new Application_Model_SetMstDb();
$array_name = $array_db->getName($emp_id);
foreach ($array_name as $name) :
$fname = $name['first_name'];
$lname = $name['last_name'];
endforeach;
$firstname = new stdClass;
$lastname = new stdClass;
$userInfo->firstname = $fname;
$userInfo->lastname = $lname;
$privilege_id = $userInfo->privilege_id;
echo 'privilege in Login: ' . $privilege_id;
$this->_redirect('index/index');
} else {
$errorMessage = "Invalid username or password";
$this->view->error = $errorMessage;
}
where getAuthAdapter() as follows:
protected function getAuthAdapter() {
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('credentials')
->setIdentityColumn('employee_id')
->setCredentialColumn('password');
return $authAdapter;
}
I want to set a session timeout.I want to set a timeout of 5 mins and when user does not being active for 5 mins then session should be expired that is logout action should be called whose code is as follows:
public function logoutAction() {
// action body
Zend_Auth::getInstance()->clearIdentity();
$this->_redirect('login/index');
}
Thanks in advance.Plz Help me.Its urgent.
When I use
$session = new Zend_Session_Namespace( 'Zend_Auth' );
$session->setExpirationSeconds( 60 );
control redirects to login page automatically after 60 seconds but I want that if the user of the application in inactive for 60 seconds then only it redirects.At present whether user is active or not redirection occurs.
I wouldn't use init() for this. init() should be use to set object state.
I would use preDispatch(). But to avoid using it all controllers or making a base controller and then extending. You could do a plugin and add it on the Bootstrap.
class YourControllerPlugin extends Zend_Controller_Plugin_Abstract {
public function preDispatch() {
//check if expired
if(hasExpired()) {
//logout and redirect
}
}
}
to add it on Bootstrap :
public function __initYourPlugin () {
$this->bootstrap('frontController');
$plugin = new YourControllerPlugin();
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
return $plugin;
}
I'm looking at my code for this right now. This snippet is from a front controller plugin. Each time an authenticated user requests a page, I reset their session expiration so they've got 60mins from they were last "active".
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request) {
//check whether the client is authenticated
if (Zend_Auth::getInstance()->hasIdentity()) {
$session = $this->_getAuthSession();
//update session expiry date to 60mins from NOW
$session->setExpirationSeconds(60*60);
return;
}
Aside: I'm looking over this code for a way to show the user a "your session has expired" message rather than the current "you're not authenticated" message.