Given that a Facebook user has logged in at some point in your Facebookapp (using fb.login) and that user has been saved in the database. How can you let the user re-authenticate with you app again after the accesstoken has expired (fb.api('/me') fails).
The signed request holds the user id, but how do you log in the user from there?
I'm using the PHP sdk and I basicly check with FB.api('/me') right in the beginning to either direct to a welcome page or the users page. But FB return false in some cases, usually after a day or 2. So I started to think it was because of the Accesstoken has expired. I have no clue how to re-authenticate, I only have knowledge that the user id still resides in the signed request.
Put following code in your javascript...it will open login popup if user is not logged in
FB.getLoginStatus(function(response) {
if (response.authResponse) {
fbUserId = response.authResponse.userID;
token = response.authResponse.accessToken;
} else {
FB.login(function(response) {
if (response.authResponse) {
fbUserId = response.authResponse.userID;
token = response.authResponse.accessToken;
}
else {
console.log('User cancelled login or did not fully authorize.');
}
});
}
},true);
Use the Javascript SDK to prompt the user to login via Facebook:
https://developers.facebook.com/docs/reference/javascript/FB.login/
Alternatively, you could try storing the code returned by Facebook during server-side auth and use it to request a new token when the user returns.
Related
I was implementing FB social login in website based on PHP. I checked FB website and found it easy to implement. Below is the approach i have followed and i am not sure that i have any security issues here.
I have used Facebook JS SDK approach.
and is as follows:
var appID = 'xxxxxxxxxxxxxxx';
window.fbAsyncInit = function() {
FB.init({
appId : appID, // App ID
channelUrl : '',
status : true,
cookie : true,
xfbml : true,
});
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
// connected
FB.api('/me?fields=id,name,email,permissions', function(response) {
//alert('Good to see you, ' + response.name + '.');
});
}else{
}
});
};
function login() {
if( navigator.userAgent.match('CriOS') ){
}
else{
FB.login(function(response) {
if (response.authResponse) {
replace_login();
} else {
// cancelled
alert('User cancelled login or did not fully authorize.');
}
},{scope: 'email,public_profile,user_friends'});
}
}
function replace_login(){
FB.api('/me?fields=id,name,email', function(response) {
$.ajax({
url: "s-account",
type: 'POST',
data: response,
dataType: 'json',
beforeSend: function(){
$("#signin_fb").button('loading');
},
success: function(data) {
$("#signin_fb").button('reset');
window.location.reload();
},
error: function(){
$("#signin_fb").button('reset');
}
});
});
}
And in PHP at server side, i am storing user detail like social_id, name and email in database through ajax call and if DB operation is successful then i am setting Session Variable in my website with username and email and user is logged in successfully.
For logout, i am using my own logout function to destroy Website user session and user is successfully logged out.
Now, where is the security risk? because if user is logged out and then try to login again JS SDK shall get a new Access Token through new response.
This whole authentication process boils down to the ajax call to s-account. You're sending name and email from FB.api() to your back end application in a POST request and as you didn't mention, I presume you're not verifying the access token with these details on the server side and you're simply making a session based on these details.
The security issue
Now, the security issue is that you're using a client side authentication on the server side. A user can simply generate a POST request to s-account with a fake response like Facebook with any username and email address and your PHP application will authenticate the user and make a valid session without verifying if the details are coming from a legit source. Your authentication is completely broken at this stage because a malicious user can login with any account by generating a simple POST request to s-account with any email and username.
How to fix
Facebook provides an end-point in the graph API which validates an access token and returns the details of the user associated with this access token. From the Docs:
This endpoint returns metadata about a given access token. This
includes data such as the user for which the token was issued, whether
the token is still valid, when it expires, and what permissions the
app has for the given user.
The Fb.Login() will generate the access token as response.authResponse.accessToken and userId as response.authResponse.userID. You need to send this accessToken & userID along with the other user details in your ajax call to s-account and then use the following API end-point to validate if the details are legit.
curl -X GET "https://graph.facebook.com/v2.6/debug_token?input_token=USER_ACCESS_TOKEN_FROM_AJAX_CALL&access_token=YOUR_APP_ACCESS_TOKEN"
If the the access token is valid, you'll get the following response with the userID for which this token was issued.
{
"data": {
"app_id": "YOUR_APP_ID",
"application": "YOUR_APP_NAME",
"expires_at": 1462777200,
"is_valid": true,
"scopes": [
"email",
"manage_pages",
"pages_messaging",
"pages_messaging_phone_number",
"public_profile"
],
"user_id": "THE_USER_ID_ASSOCIATED_WITH_THE_TOKEN"
}
}
Now you can compare this userID with the userID you received in the ajax call and check if the details are legit.
How to get your APP_ACCESS_TOKEN
To debug the user access token using the debug_token API, you need to generate your APP access token on the server side using the following API end-point.
curl -X GET "https://graph.facebook.com/v2.6/oauth/access_token?client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&grant_type=client_credentials"
This will return your app access token in response.
{
"access_token": "YOUR_APP_ACCESS_TOKEN_HERE",
"token_type": "bearer"
}
Im using the javascript SDK of facebook in the frontend:
jQuery("#pct_fbbutton").live("click",function(){
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
console.log('Conected');
FB.api('/me/likes', function(response) { user_likes = response;
FB.api('/me', function(response) { user = response;
send_server(user, user_likes);
});
});
} else if (response.status === 'not_authorized') {
console.log('not_authorized');
login();
} else {
console.log('not_logged_in');
login();
}
});
});
As you can see, after the user is login with facebook ill send two objects via AJAX to a php script (using the function send_server).
I can access in the backend the currently generated token with $facebook->getAccessToken(), but as I know this is retrived from a cookie made by the javasript sdk, considering that all frontend data can be hacked, using the token how can ensure that the user data is valid on php?
The answer is to perform the graph calls with PHP instead of JavaScript, especially if the only purpose of your client-side script is to send it to the server;
Doing the data gathering on the server is the only practical way you can be sure that the data has not been tampered with, assuming you do proper https certificate checking.
The validity of the access token is also easy, because Facebook will return an error if the provided access_token value is invalid.
Once you retrieve the access_token in the back end, try to fetch the user's profile. If the fetch was successful, then the client provided you with the correct access_token and you should save it. Something like the below example should work, but you can find many more examples:
<?php
$facebook = new Facebook(array(
'appId' => $initMe["appId"],
'secret' => $initMe["appSecret"],
));
$facebook->setAccessToken($initMe["accessToken"]);
$user = $facebook->getUser();
if ($user) {
$user_profile = $facebook->api('/me');
print_r($user_profile);
}
?>
I'm having some troubles with the Facebook API, I am requesting permission for some information of the user by Javascript:
FB.login(function (response) {
if (response.authResponse) {
// authorized
} else {
// canceled
}
}, {scope: 'email,user_birthday,user_location'});
Now I want to get the user's information (when he is authorized) by PHP:
$facebookUser = json_decode(file_get_contents('https://graph.facebook.com/' . $this->data['userid'] . '?access_token=' . $this->oathtoken . '&fields=id,name,location,gender,email,picture,birthday,link'));
All working great, except that the API is not giving the user's birthday back. Even when I set my birthday privacy settings on public it won't show up.
So:
echo $facebookUser['birthday']; // Gives an error, since this key does not exists
Does somebody have an idea how to fix this?
You are logged in on the client side but php is executed on the server side so you receive only the basic information of an user object.
If you logged in on the server side and have the permissions so you can get the birthday simply with:
echo $facebookUser['birthday'];
Most probably you didn't receive approval to request user_birthday from FB.
You can't get user_birthday unless FB reviews your FB app.
Only the following permissions may be requested without review: public_profile, user_friends and email
(See: https://developers.facebook.com/docs/facebook-login/review/what-is-login-review)
Without review you can request only age_range, but not user_birthday
FB.login(function (response) {
var user_birthday = response.user_birthday;
console.log(user_birthday);
if (response.authResponse) {
} else {
// canceled
}
}, {scope: 'email,user_birthday,user_location'});
why don't you use serverside auth if you would like get FB info in server side?
if you are ok with clientside authentication, above should work.
I am having trouble using permissions to upload to a specific Facebook album.
I am set to an admin to a specific Facebook page, however whenever I try to upload a photo I cannot seem to get access to do it.
The error I get is:
Fatal error: Uncaught OAuthException: (#120) Invalid album id thrown
If I explore using the Graph explorer, I can see this album without any access token required.
The URL I am using is https://graph.facebook.com/albumId/photos
The album I am trying to upload is an album within a Facebook page, which I created within Facebook.
The permissions I have set are:
user_photos
friends_photos
publish_actions
manage_pages
publish_stream
photo_upload
Any help appreciated.
First, check if the user is logged in:
function getLoginStatus() {
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
// the user is logged in and connected to your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
getPageAccess();
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
//but not connected to the app
logIn();
} else {
// the user isn't even logged in to Facebook.
logIn();
}
});
}
Then get the page access token associated to the user:
function getPageAccess() {
FB.api('/PAGE_ID?fields=access_token', function (resp) {
var pageAccessToken = resp.access_token;
});
}
This seems a common issue. If you want to impersonate Facebook Page (that is, act as page itself for uploading, posting and sharing) you have to first obtain an access token for that page:
Issue a GET Request to '/me/accounts' using user access token
Loop the result set and look for the page (pay attention to category property of each item, skip "application" as it's considered an account)
Store access token for that page.
Then (using PHP SDK):
$page = '432652662'; // Your page id
$album = '128949205'; // Your album id
$source = 'mypic.png';
$payload = array('source' => '#' . $source,
'access_token' => 'page access token');
// Upload photo for specific page and album
$sdk->api("/$page/$album/photos", 'POST', $payload);
Ok, I think I have found the solution.
If you go to https://graph.facebook.com/me/accounts
Each account will have its own access_token. When uploading an image to a Facebook page, you will need to pass that access_token as a parameter.
I currently have the facebook js sdk to login to the site and then I want to use the PHP sdk for everything else. How can I pass the accessToken to the PHP SDK to allow me to do this? I want to pass it to a PHP script at a different url, so for example I login at /login/ and then open up a modal box to /facebook/?accessToken=gfdhjsfghjkfgdh or what ever.
http://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
Please check this link.
A sample code may look like this,
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
//got the token
var accessToken = response.authResponse.accessToken;
//send it to php via _GET
window.location = "/facebook/?accessToken=" + accessToken;
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
//but not connected to the app
} else {
// the user isn't even logged in to Facebook.
}
});
You can get the accesstoken then send it to php.