I'm currently asking users for two read permissions i.e. email and user_location and one write permssion i.e. publish_actions. Following is the code snippet I'm using to verify if the user has granted all requested permissions:
$facebook = new Facebook(APP_ID, APP_SECRET, REDIRECT_URI);
if ( $facebook->IsAuthenticated() ) {
// Verify if all of the scopes have been granted
if ( !$facebook->verifyScopes( unserialize(SCOPES) ) ) {
header( "Location: " . $facebook->getLoginURL( $facebook->denied_scopes) );
exit;
}
...
}
Facebook is a class I've customly built to wrap the login flow used by various classes in the SDK. IsAuthenticated() makes the use of code get variable to check if the user is authorized. verifyScopes() checks granted permissions against SCOPES and assings an array of denied scopes to denied_scopes property. getLoginURL()` builds a login-dialog URL based on permissions passed as an an array as a only paramter.
Now, the problem is when the user doesn't grant write permissions, publish_actions in this case, write permission dialog is shown until user grants the write permission. But if the user chooses to deny of the read permissions, say email, the read login dialog isn't show. Instead Facebook redirects to the callback URL (that is REDIRECT_URI) creating a redirect loop.
The application I'm builiding requires email to be compulsorily provided but apparently the above approach (which seems to be the only) is failing. So, is there a workaround or a alternative way to achieve this? Or Facebook doesn't allow to ask for read permissions once denied?
As of July 15, 2014, an update has been made to the Facebook PHP SDK 4.x that allows user to re-ask the declined permissions. The function prototype of getLoginUrl() now looks like this.
public function getLoginUrl($redirectUrl, $scope = array(), $rerequest = false, $version = null)
So, to re-ask declined permissions we'd do something like this:
<?php
// ...
$helper = new FacebookRedirectLoginHelper();
if ($PermissionIsDeclined) {
header("Location: " . $helper->getLoginUrl( $redirect_uri, $scopes, true );
exit;
}
// ....
?>
For the time being you can append &auth_type=rerequest to the getLoginUrl return value to enable a rerequest, kind of lame but it works.
$helper = new FacebookRedirectLoginHelper($app_url, $app_id, $app_secret);
$login = $helper->getLoginUrl(array('scope'=>'user_likes', 'user_location'));
print $login."&auth_type=rerequest";
what about getReRequestUrl(); ?
That works just fine.
Read more at https://developers.facebook.com/docs/php/FacebookRedirectLoginHelper/5.0.0
Related
I am using Facebook as external login of ASP.Net Core identity.
I would like, even if the user logged in with Facebook, the user to fill his profile on the website.
For that I use the ExternalLoginCallback method, from which I would like to get data from Facebook such as date of birth, location (country), ...
One issue is if the user unchecked some of the permissions, the default call to Facebook fails:
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
return RedirectToAction(nameof(Login));
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
I would also need to do some additional checks on user data, which would require calling directly the Graph API.
My blocking points:
- In the ExternalLoginCallback method, I would need to separate the 'country' and 'birthday' to avoid the Facebook API to return an error in case of the user didn't grant the permission
- For that I would need the the user access_token (and for additional calls in the method), I don't see how to get it even if it is used by the Facebook Identity framework
- Once the profile created, I would like to get access to this access_token, which should be stored in the UserTokens table (I guess?), but I can't find it there, the table is empty. (my DbContext is a class extending IdentityDbContext<AppUser, AppRole, long>, don't know if it has an impact)
I have found this answer https://stackoverflow.com/a/42670559/4881677 which may help, but not sufficient.
Any help? :)
In order to store the user Facebook token, it requires to specify it in the options (not stored by default).
var fo = new FacebookOptions();
fo.SaveTokens = true;
From there we can call the graph method permissions to get the available permissions: https://graph.facebook.com/me/permissions?access_token={token}
Then it can be read with something like this:
foreach (var perm in data)
{
perms.Add((string)perm["permission"], (string)perm["status"]);
}
I' m having a difficulty in getting a clear view on Joomla (I'm using version 3.6.x) authentication and login.
I've seen many approaches and snippets on how to "Login" in a Joomla site from an external source (which is what i m interesting in).
But what i see after testing and testing (and more testing), is that at the end of the day, you re simply not logged in.
You may have the ability to verify that the user who's attempting to login is a valid registered user and also to be able to retrieve his name, email, phone and blah blah blah but in Joomla backend, this user is still not logged in.
I created a brand new user in Administrator Panel (it shows the user NEVER visited the site), I wrote the login script in external PHP, I "authenticated" the new user a dozen times and when i go back to the Backend, the user still NEVER been there. Not even once.
Which, i guess, it also means that i can't retrieve a list of "active" users, since Joomla can't see them as logged in users.
So what i want to achieve is users to be able to login from outside the Joomla site and to have the ability to know IF they're logged in or not. So I can get a list of them and assign tasks to them.
I guess it has something to do with tokens and cookies. But no matter how much i searched to that direction, i can't find any examples to enlighten me.
Any help (and specially "scripting" help) is much appreciated.
Thank you.
<?php
if (version_compare(PHP_VERSION, '5.3.1', '<')) {
die('Your host needs to use PHP 5.3.1 or higher to run this version of Joomla!');
}
define('_JEXEC', 1);
if (file_exists(__DIR__ . '/defines.php')) {
include_once __DIR__ . '/defines.php';
}
if (!defined('_JDEFINES')) {
define('JPATH_BASE', __DIR__);
require_once JPATH_BASE . '/includes/defines.php';
}
require_once JPATH_BASE . '/includes/framework.php';
$app = JFactory::getApplication('site');
$app->initialise();
require_once (JPATH_BASE .'/libraries/joomla/factory.php');
$credentials['username'] = $_GET["usrname"];
$credentials['password'] = $_GET["passwd"];
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('id, password')
->from('#__users')
->where('username=' . $db->quote($credentials['username']));
$db->setQuery($query);
$result = $db->loadObject();
if ($result) {
$match = JUserHelper::verifyPassword($credentials['password'], $result->password, $result->id);
if ($match === true) {
$user = JUser::getInstance($result->id);
$session =& JFactory::getSession();
echo "<p>Your name is {$user->name}, your email is {$user->email}, and your username is {$user->username}</p>";
$session->set('userid', $user->id);
if ($user->guest) {
echo 'SORRY, NO LOGIN YET...';
} else {
echo 'user is LOGGED IN !';
}
} else {
die('Invalid password');
}
} else {
die('Cound not find user in the database');
}
?>
Joomla log in is handled in authentication plugins, by writing and enabling your own you can bypass the token verification and allow logging in by a simple get request.
Keep in mind that Joomla! keeps administrator and frontend sessions separate (you can be logged into the administrator, and browse the site as a guest). Additionally, a user needs a Manager, Administrator or Super User group membership to login to the backend.
Administrator
The common use case for admin login is to run a background script.
In order to bypass the Joomla Authentication in the backend, check out the /cli folder as it contains some examples.
Frontend
The use case for frontend login is much wider: server-to-server communication, app authentication, single sign on etc.
The best practice to perform frontend authentication is to build your own authentication plugin. The authentication will be implemented by the onUserAuthenticate() function. Check out the /plugins/authentication/joomla/ plugin for an example.
If you need extra data for the authentication plugin to perform its magic (e.g. the remote token/api key to authenticate using a remote service) you might want to check out the user plugin group, for example the /plugins/user/profile plugin.
Verify last login date/time
The frontend approach should automatically work, showing users as logged in in the administrator control panel.
However, if you use the CLI approach to login to the administrator, this may not work depending on how you perform login.
To be able to check if a user is logged in only authentication is not enough but you need a forced login. Only then you will be able to view them at the backend.
This is the code you can use to check if a user is logged in or not
<?php
if (version_compare(PHP_VERSION, '5.3.1', '<')) {
die('Your host needs to use PHP 5.3.1 or higher to run this version of Joomla!');
}
if (!ini_get('display_errors')) {
ini_set('display_errors', '1');
}
echo ini_get('display_errors');
define('_JEXEC', 1);
define('JPATH_BASE', "C:\Vertrigo\www\joomla" );//Define the Base path as per your installation directory
if (!defined('_JDEFINES'))
{
require_once JPATH_BASE . '/includes/defines.php';
}
require_once JPATH_BASE . '/includes/framework.php';
// Instantiate the application.
$app = JFactory::getApplication('site');
$app->initialise();
require_once (JPATH_BASE .'/libraries/joomla/factory.php');
//Check if a user exists else create one with forced login
$user = JFactory::getUser();
jimport('joomla.plugin.helper');
$credentials = array();
$credentials['username'] = JRequest::getVar('username', '');
$credentials['password'] = JRequest::getVar('passwd', '');
if (!$user->get('gid')){
$forcevars = array();
$forcevars['silent'] = true;
$forcevars['forecelogon'] = true;
$response = $app->login($credentials, $forcevars);
if($response){
echo "Login Successful";
}else{
echo "Login Unsuccessful. Check Username and Password. Give just Plain password.";
}
}
?>
I have modified the code as you neeeded to call username and password in url. better to use getVar in joomla rather than GET method. You url will be in the format http://www.yoursite.com/thisscript.php?username=yourusername&passwd=yourpassword
Context
From Facebook Best Pratices I've understand that I should request a minimum set of permissions on the initial login page and delay the request of extended permissions for when they are really required.
For example. Say that my original login request for two extended profile properties:
<fb:login-button show-faces="true" width="200" max-rows="1"
scope="user_photos, friends_photos">
</fb:login-button>
Now, at some latter point, the user wishes to upload a photo back their profile using my app (which requires publish_actions).
As I understand it my App have to:
Check if the user have already granted that permission (say, with FB.api('/me/permissions')) to avoid triggering a login flow for no reason
Ask for the new permission by means of performing a new login:
FB.login(function(response) {
// handle the response
}, {scope: 'publish_actions'});
Perform a second permission check to see if the user have granted the new permission. If the user have granted the new permission perform the upload, else display some kind of error message explaining to the user that he should grant the new permission before being able to upload.
And my doubts are:
On the second step outlined above, should I ask only for publish_actions or should I also request already granted permissions?
{scope: 'user_photos, friends_photos, publish_actions'});
I'm using a Login on Client, API Calls from Server model:
So, at the moment that I request the new permission, my server will be holding a long-lived access token with the two initial permissions (user_photos, friends_photos). If the user grants publish_actions, am I supposed to go through the entire server side token exchange process again (using the new short lived access-token) before uploading?
GET /oauth/access_token?
grant_type=fb_exchange_token&
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={new-short-lived-token}
Or will the new permission be promptly available for the long-lived token?
Answering my own question to anyone facing the same problem.
Yes, granted permissions are cumulative.
Should I ask only for publish_actions or should I also request already granted permissions?
I only need to ask for new permissions. Previously granted permissions remain available.
If the user grants publish_actions (with FB.login), am I supposed to go through the entire server side token exchange process again (using the new short lived access-token) before uploading? Or will the new permission be promptly available for the long-lived token?
The client side call to FB.login suffices. If the user grants publish_actions I can use the previously stored server side token to post content.
One small gotcha: When you call FB.login the user may skip the permission again (and it will return response.status === "connected"). So I had to actually double check for permissions:
Function to check permissions with callbacks:
function checkPermissions(perms, callback, failCallback) {
FB.api('/me/permissions', function (response) {
var fbPerms = response.data[0];
var haveAllPermissions = true;
if (typeof perms === 'string') {
perms = [ perms ];
}
for (var i in perms) {
if (fbPerms[perms[i]] == null) {
haveAllPermissions = false;
break;
}
}
if (haveAllPermissions) {
callback();
} else {
failCallback();
}
});
}
Assuming that photoUpload is a function that requires the publish_actions permission, here is the usage pattern that worked for me:
// First check
checkPermissions("publish_actions",
// If the required permissions have already been granted
// call the desired method
photoUpload,
// else
function () {
// Asks for permission
FB.login(function () {
// double check - the usar may have skiped the permission again
checkPermissions("publish_actions",
// if the user granted the permission, call the desired method
photoUpload,
// else cancel everything and warn the user
function () {
alert("Can't post without permissions");
});
}, {scope: "publish_actions"});
});
I am trying to post on user wall on behalf of the application.
Here's what I am doing.
First I get the user access_token from facebook connect and then I reciever an extended access token by using the following call
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
Now when I try to post on friends wall with the following code.
var params = {};
params['message'] = 'Check out this new gag I\'ve posted about you surely it will piss you off :p';
params['name'] = 'Gagsterz';
params['description'] = 'If you also want to have fun on gagster join in now.';
params['link'] = 'http://localhost/php/backup/View/Profile.php';
params['picture'] = thumbpath;
params['caption'] = 'Caption';
FB.api('/'+victimid+'/feed?access_token="ACCESS_TOKEN", 'post', params, function(response) {
if (!response || response.error) {
var json = $.parseJSON(response.error);
alert(json.code);
alert(JSON.stringify(response.error));
} else {
alert('Published to stream - you might want to delete it now!');
}
});
It gives the following error.
the target user has not authorized this action facebook graph api
with code 200.
I take the permission publish_stream and I've tested it by reverting the permission and then logging in to give permissions to it again and the login windows asks for post on behalf permission.But I am taking the permission while I am retrieving the access token for the first time. Do I have to take the permission again for extended access token ? if yes then how to do so ?
Check your permissions, Do you have permission to Post on wall. By default you will have only read permission.
Check this http://developers.facebook.com/docs/reference/rest/stream.publish/
i have this URL ..
https://graph.facebook.com/602414132/albums?access_token=123005381082600|2.FUPHmHF4kfDPly2GRPYDeg__.3600.1298228400-602414132|1JUKjvfo1Ri5s04x9v_vzf-sS8c&type=post&name=refacingme
when i using Chrome i get empty response :
{
"data": [
]
}
from where do you think the error is coming ??
It is true, I just fixed this bug a few days ago myself. The user must grant Extended Permissions for user_photos on your app. A few ways to do this:
1) Update your app info in FB Developer tab and add Extended permissions to your app. This will ask new users who register to give certain permissions before joining.
2) Alternatively, if the user has already added the app you can ask the user for permissions using the Javascript SDK call FB.login while they are in the app. (see code below)
FB.login(function(response) {
if (response.authResponse) {
// user is logged in and granted some permissions.
console.log("user is logged in and granted some permissions.")
} else {
// User cancelled login or did not fully authorize.
console.log("he failed to login or something")
}
}, {scope:'user_photos'});
This route requires you have set up the Facebook JavaScript SDK
I think you have not taken Extended Permission of user_photos. Take extended permission then you will not receive empty array