Validating the user of an access_token - facebook

In the Facebook dev article Manually Building a Login Flow, there is a section entitled "Confirming Identity". It mentions that you need to validate codes and tokens that you receive from them via your redirect_uri.
My question: Since you don't know anything about the user that just logged in, how do you validate that the user_id that you see in the response from the token inspection endpoint is correct?
The article says:
As a result, your app should confirm that the person using the app is the same person that you have response data for before generating an access token for them.
But, how can you actually do that? Are we expected to show publicly available info about that user_id back to the user with a UI that asks "Is this you?". I haven't seen any apps/sites that do that, so I'm assuming that this isn't practically done.
Am I missing something?

You can use FB.getLoginStatus to retrieve information about the logged in user. It returns a response object for the user. If the user has authenticated your application, the response object will look like this:
{
status: 'connected',
authResponse: {
accessToken: '...',
expiresIn:'...',
signedRequest:'...',
userID:'...'
}
}
You can use the UserId returned in this object to verify the user's identity.

Related

Matching a page-scoped ID and a chatbot-scoped ID: several questions

I have a web site (gigsnet) with a Facebook login. I did it using Passport/oauth.
I want to implement a chatbot, so that our users can chat to the "Gigsnet" page, and get personalised responses from the chatbot.
I realise that the IDs won't match, and that some "magic" is necessary here. So, here we go.
SOME IDS
This is what my server receives when a message by myself (page admin) arrives:
Requested: /app/chatbot
REQUEST BODY: { object: 'page',
entry:
[ { id: '1130858117040228',
time: 1495584613258,
messaging:
[ { sender: { id: '119XXXXXX7137093' },
recipient: { id: '1130858117040228' },
timestamp: 1495584613013,
message:
{ mid: 'mid.$cAAQEgkm16DpiaQ9qlVcN8ohagocY',
seq: 290123,
text: 'd' } } ] } ] }
POST /app/chatbot 200 1.581 ms - -
This tells me that my user ID for the chatbot is 119XXXXXX7137093.
Also, this is what I have on my DB for authenticated users:
{ "_id" : ObjectId("5924c947b0d5a1095c8f326c"), "strategyId" : "facebook", "field1" : "636379230", "field3" : "EAAGcvqGZBOtkBAFuHIC4U9CxB8vEFcL25iQ6vsVkT9s1CKfJtqPPg6YhJjZAepu2v6EqtOjj6UDCaTYxca9UbRReIhKeIu4UGmEW4L9lJcGDXXXXXXXXXXXXXXXl4YgoMx8ExmI0zZCEsbZBYtZA9SR3PW9FkMdZCkgZD", "userId" : ObjectId("59239720a04b2e1de8e103d8"), "id" : ObjectId("5924c947b0d5a1095c8f326b"), "_children" : { }, "_clean" : true }
This particular user happens to be admin of the pages, the app, and owner of the company that owns page and app.
So:
USER ID (CHATBOT SCOPE): 119XXXXXX7137093
USER ID (LOGIN APP SCOPE): 636379230 WITH ADMIN ACCESS TOKEN: EAAGcvqGZBOtkBAFuHIC4U9CxB8vEFcL25iQ6vsVkT9s1CKfJtqPPg6YhJjZAepu2v6EqtOjj6UDCaTYxca9UbRReIhKeIu4UGmEW4L9lJcGDXXXXXXXXXXXXXXXl4YgoMx8ExmI0zZCEsbZBYtZA9SR3PW9FkMdZCkgZD
PAGE ID: gigsnetco
APP ID (LOGIN APP): 453817548028633
The access token is important as I am the admin for the pages.
USER ID IN MESSENGER BOT -> USER ID IN LOGIN APP (with extra step)
To make this call, I need FIRST an access token for the page. So, here I am using my user's oauth access token from the database in order to get another access token, the one to administer pages.
curl "https://graph.facebook.com/v2.9/gigsnetco?access_token=EAAGcvqGZBOtkBAFuHIC4U9CxB8vEFcL25iQ6vsVkT9s1CKfJtqPPg6YhJjZAepu2v6EqtOjj6UDCaTYxca9UbRReIhKeIu4UGmEW4L9lJcGDXXXXXXXXXXXXXXXl4YgoMx8ExmI0zZCEsbZBYtZA9SR3PW9FkMdZCkgZD&fields=access_token"
{"access_token":"EAAGcvqGZBOtkBAMeNZAE09EsRndMYvDlVpNeE6HT05zxqeL2XXXXXXXXXXXXXXXXDs2vdiSYlErrdw8ZAF3uBdUiIjnioXxohljA3mLjlKbgFZC5tfz7ivGHCHb4LubXHBNx7wLMMLcl7WrZC627uca2HW18dQZBH5MGhOqvHRyYXQZDZD","id":"1130858117040228"}
I now use the token for the page ID above to make a request on the user 119XXXXXX7137093 (chatbot) asking it the list of ids for linked pages.
Note: I am using the user id 119XXXXXX7137093, filtering by app ID, using the page's access token.
curl "https://graph.facebook.com/v2.9/119XXXXXX7137093/ids_for_apps?app=453817548028633&access_token=EAAGcvqGZBOtkBAMeNZAE09EsRndMYvDlVpNeE6HT05zxqeL2XXXXXXXXXXXXXXXXDs2vdiSYlErrdw8ZAF3uBdUiIjnioXxohljA3mLjlKbgFZC5tfz7ivGHCHb4LubXHBNx7wLMMLcl7WrZC627uca2HW18dQZBH5MGhOqvHRyYXQZDZD"
{"data":[{"id":"636379230","app":{"link":"http:\/\/www.gigsnet.com\/","name":"Login","id":"453817548028633"}}],"paging":{"cursors":{"before":"NDUzODE3NTQ4MDI4NjMz","after":"NDUzODE3NTQ4MDI4NjMz"}}}
Bingo! I have 636379230. When a user types anything to the chatbot, I can now cross-reference them with a user who has previously logged in, and provide personalised information.
USER ID IN LOGIN ALL -> USER ID IN MESSENGER
Here I am using the access token from the screen https://developers.facebook.com/apps/453817548028633/messenger/ in "Token generation".
curl "https://graph.facebook.com/v2.9/636379230/ids_for_apps?app=453817548028633&access_token=EAAGcvqGZBOtkBAKycZAeDI4D13sOVZAZAlNJqZCTXXXXXXXXXXXXXXXxsyaOs9WgQO2jcnHJeEYAbUlEdCU1xejYjTlWE1w4tEfe7IZAAw8i3X2qn1AKwDlHhwHbQsoylxPZAcPagVKemSd6KQwUP2gB8pkm61PumqLiVuIw3B3lQZDZD"
{"data":[{"id":"636379230","app":{"link":"http:\/\/www.gigsnet.com\/","name":"Login","id":"453817548028633"}}],"paging":{"cursors":{"before":"NDUzODE3NTQ4MDI4NjMz","after":"NDUzODE3NTQ4MDI4NjMz"}}}
Note that my own access token (I guess because I am the page admin) ALSO works:
curl "https://graph.facebook.com/v2.9/636379230/ids_for_apps?app=453817548028633&access_token=EAAGcvqGZBOtkBAFuHIC4U9CxB8vEFcL25iQ6vsVkT9s1CKfJtqPPg6YhJjZAepu2v6EqtOjj6UDCaTYxca9UbRReIhKeIu4UGmEW4L9lJcGDXXXXXXXXXXXXXXXl4YgoMx8ExmI0zZCEsbZBYtZA9SR3PW9FkMdZCkgZD"
{"data":[{"id":"636379230","app":{"link":"http:\/\/www.gigsnet.com\/","name":"Login","id":"453817548028633"}}],"paging":{"cursors":{"before":"NDUzODE3NTQ4MDI4NjMz","after":"NDUzODE3NTQ4MDI4NjMz"}}}
Bingo! 636379230 is the user ID for messenger.
QUESTIONS
The API documentation mentions that I should provide appsecret_proof in order to get ids_for_pages to work. However, I didn't have to (plus, you only provide a PHP example, and I couldn't find an example in node). Is it OK not to provide appsecret_proof?
In order to make calls, I am using my own access token (as the page admin); I obtained this access token using oauth, using my app (I am using passport on nodejs).
Will this token expire after 60 days?
If so, does that mean that I ought to link/unlink my account so that I generate a new access token? (Or, use an API command to get a new access token? (If so, which one?)
Since the access token expires, shall I keep on refreshing it for logged in users as well? The problem is that once a user is logged in, they are alogged in.
Does the page access token also expire? How often shall I re-generate it? Surely, regenerate it for every received message would be wasteful on my end and your end...!
Are scoped IDs stable? Do I absolutely know that the IDs will never change? Once I make the connection, can I save it and have it like that forever? Even if a user unlinks themselves with oauth and then link themselves back? Or in case the access tokens expire for example. If they do change, how do I know if they are expired/?
Thank you!

Facebook debug_token only returns is_valid and app_id

I am trying to return the user_id in my app when calling https://graph.facebook.com/v2.2/debug_token
The Fields in the return data only returns is_valid and the app_id like this:
{
"data": {
"app_id": "338666432978837",
"is_valid": true
}
}
Is there a setting that I am missing in the App setup?
It seems like you are debugging your own app access token, this is the reason why you don't get any user specific info - because there is no user in this context.
In order to get user info such as the user's id, you need to debug the user access token:
GET /debug_token?
input_token=THE_USER_TOKEN&
access_token=YOUR_APP_TOKEN
input_token The user's token you want to debug.
access_token Your app's token. Required for request authentication.
Do you use
GET /debug_token?
input_token={input-token}&
access_token={access-token}
as described in https://developers.facebook.com/docs/facebook-login/access-tokens#debug
I was able to reproduce this behaviour if the input_token parameter is an app access token, so what kind of input_token are you using?

How to verify user login when using Facebook Javascript SDK

I've already gone through this question, but my question is different. I am unsure of how can javascript login be sure and how can some-else not login into others account.
According to the getting started FB.authResponse is called when the login is successful, but in the client's side of course.
Then we can get the userId & accessToken out of the response, we can also make call to /me to get more information. In order to put that user into session, all this info about successfull javascript login has to be sent to the server and this is where I get confused.
After all it's HTTP, every other request is different and can be replicated.
May be it's just that I'm confused, about how someone can't hack and immitate any other users' facebook id to login into his account.
For e.g. after the authentication is success, I make an ajax call to my server providing
the fb-user-id and then I match it with the database and put the appropriate user in the session, but not since this is fb-user-id is not verified again in the back-end (or is it verified?, I didn't found anything though about it) that this particular user is the one who actually signed up in my application, then a same login request with someone else's fb-user-id can be made to login into his account.
I'm sure, I'm not the first one to have this confusion. Please help to clear this confusion, as I've read the docs many times now, but still unable to figure out why can't someone else login into someone else's account.
EDIT
I found this similar question but the guy here doesn't answer how he verified backend login or may be I was unable to understand.
According to :
How to securely authorize a user via Facebook's Javascript SDK
Send the signed_request field to your server, which is being received in the authResponse using the javascript sdk
Then in the server-side, the following procedure as stated in the documentation has to be followed for verfication :
Once you have captured the signed request, you need to perform three steps:
Split the signed request into two parts delineated by a '.' character (eg. 238fsdfsd.oijdoifjsidf899)
Decode the first part - the encoded signature - from base64url
Decode the second part - the 'payload' - from base64url and then decode the resultant JSON object
Here is an example in PHP:
function parse_signed_request($signed_request) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$secret = "appsecret"; // Use your app secret here
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
// confirm the signature
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
This will produce a JSON object that looks something like this:
{
"oauth_token": "{user-access-token}",
"algorithm": "HMAC-SHA256",
"expires": 1291840400,
"issued_at": 1291836800,
"user_id": "218471"
}
After getting the user_id, that particular user can be put in session, although there needs to be other checks for proper authorization.
As a second check, the issued_at can be checked to see if it's not more than 10 mins old.
Taken from here.
However, there may be scenarios where your app_secret may be
compromised. To take care of this case, you should follow step #3, as
the exchange of code for access_token can happen only once and within
10 mins of it's issue. If the user doesn't have an account with your
site, then you anyway need step #3 to use the access_token for
retrieving other necessary user data, like name, email, etc from FB.
In order to refresh token the following call can be made from your server
GET /oauth/access_token?
grant_type=fb_exchange_token&
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={short-lived-token}
Reference
When making a graph API call you need an access_token that is unique to an user and an app.
When query a call concerning a user-id and just manipulate it, the access-token you are using belongs to the original user and Facebook API just returns the information the manipulated id may get. This may be different if the manipulated user is a friend of the original or not.
Simplified:
Your user logs in at your website and accepts your scope params.
Now Facebook returns a user-id and an user access_token that is valid just to this user and your one app.
Most graph calls now needs a user-id and an access_token. If you do an update request for example on the users friend lists and the access_token does belong to the user, graph api will return an error.
It you store the access_token on your server and send it to your server via HTTP-Request / AJAX and not via https, a man in the middle could catch the access-token and abuse it.

Facebook API fails and Graph works. Why?

I have a Facebook app that works fine when I call $facebook->api('/me'), it returns all the user information, but it fails when I call $facebook->api('/100006737731259'). The error I get is:
array("error" => array("message" => "Unsupported get request.", "type" => "GraphMethodException", "code" => 100))
And the strange thing is that if I open my browser and go to http://graph.facebook.com/100006737731259 it returns all the information with no problem (it is one test user for my app).
Have you ever had a problem like it? I do not know what can I be doing wrong.
Thank you very much
When call this API for test user, you can put empty access token OR just don't put access_token parameter at all, then you can solve it.
If you really want to put the access_token, there's the rule you have to follow:
Prohibited access_token:
Normal user's access token
Other app's test user access token
Other app access token
Allowed access_token:
Test user's access token(Either the test user is current app's other test user or this test user 100006737731259, both is allowed!) retrieved from https://graph.facebook.com/APP_ID/accounts/test-users?installed=true&name=TEST_USER_NAME&locale=en_US&permissions=read_stream&method=post&access_token=APP_ACCESS_TOKEN (Replace the relevant APP_ID, TEST_USER_NAME, and APP_ACCESS_TOKEN)
Current App Access token
*APP_ACCESS_TOKEN can be retrieved from https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=APP_SECRET&grant_type=client_credentials (Replace the relevant APP_SECRET)
**App Secret can be get from https://developers.facebook.com/x/apps/APP_ID/settings/ (Replace relevant APP_ID)
The proof is, if you request with other user access token, https://graph.facebook.com/100006737731259?access_token=PROHIBITED_ACCESS_TOKEN at web browser, you would get error eventually.
But if you do https://graph.facebook.com/100006737731259?access_token=Allowed_ACCESS_TOKEN OR https://graph.facebook.com/100006737731259?access_token= (left the access_token value empty) with your web browser, then you can get the data.
This is a problem that only occurs with test users. I don't know why, but it is. If you use a real user, this will not happen.

Facebook access token server-side validation for iPhone app

I'm developing iPhone application, that is based on communication with server, and I want to use Facebook authentication mechanisms.
Basically, I think it should work like this:
In my iPhone app, user logs in to Facebook, using his email and password.
User allows access to his data for related Facebook application.
My iPhone app receives access token, after successful log in.
In further communication with my server, my iPhone application should use the received Facebook access token (for example: in queries).
When my server receives some query from iPhone app, with access token, it should ask Facebook that this token is valid (and for who), and if yes, server should assume that user is authenticated with Facebook.
My question is: how the server should ask Facebook if given access token is valid? I think I should somehow check if the token is valid for my Facebook app.
I've tried many Facebook queries to graph API, that I've found, but nothing worked as I expected. Can you provide me some example?
Here's a two step process you can use to validate that a user access token belongs to your App:
1) Generate an App Access token
(https://developers.facebook.com/docs/howtos/login/login-as-app/)
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID
&client_secret=YOUR_APP_SECRET
&grant_type=client_credentials
2) Debug the User Access token
(https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/)
https://graph.facebook.com/debug_token?
input_token=INPUT_TOKEN
&access_token=ACCESS_TOKEN
Where INPUT_TOKEN is the user access token you want to verify, and ACCESS_TOKEN is your app's token that you got from step 1.
The debug endpoint basically dumps all information about a token, so it'll respond with something like this:
{
data: {
app_id: YOUR_APP_ID,
is_valid: true,
metadata: {
sso: "iphone-safari"
},
application: YOUR_APP_NAMESPACE,
user_id: USER_ID,
issued_at: 1366236791,
expires_at: 1371420791,
scopes: [ ]
}
}
If that token isn't from "your app" then it will return an error response.
Update: this answer seems insecure since it doesn't validate the token
first as belonging to your app, see the comments, original answer as
follows:
I assume that you already have the access token in hand. In such a case the simplest way to validate an access token is to issue the following request
https://graph.facebook.com/me?fields=id&access_token=#accesstoken
Here replace #accesstoken with the access token you have. I will breakdown the url and will explain each.
We are issuing a graph api request here which will return the Facebook User Id of the owner of the access token as a JSON string. The keyword 'me' represents the currently logged in user or the owner of the access token. For this request access token is a mandatory parameter.
If the provided access token is not valid or expired Facebook will just return an error message of some sort.
For a valid access token the result will somehow look like this
{
"id": "ID_VALUE"
}
Another solution would be to use https://graph.facebook.com/app/?access_token=[user_access_token] as described by Get application id from user access token (or verify the source application for a token).
This appears to be an undocumented feature, but returns JSON containing the id of the app the token was generated for. If the token wasn't for your app, it returns a 400.
In the latest version of facebook (2.2) you can do it this way:
https://developers.facebook.com/docs/graph-api/reference/v2.2/debug_token
Sample output:
{
"data": {
"app_id": "THE APP ID",
"application": "APP NAME",
"expires_at": 1427245200,
"is_valid": true,
"scopes": [
"public_profile",
"basic_info",
"read_stream",
"email",
"publish_actions",
"read_friendlists",
"user_birthday",
"user_hometown",
"user_location",
"user_likes",
"user_photos",
"user_videos",
"user_friends",
"user_posts"
],
"user_id": "THE USER ID"
}
}
private function facebookRequestMe($access_token)
{
include_once "facebook.php";
$facebook = new Facebook(array(
"appId" => "your_application_id",
"secret" => "your_application_secret"
));
$facebook->setAccessToken($access_token);
return $facebook->api("/me", "GET");
}
You can download the Facebook SDK for PHP from GitHub.
If a user has passed you a Facebook UID that they claim is theirs and you want to check if it's legit, this is a Python function that will verify it against their access token (an implementation of Robin Jome's answer):
def verify_facebook_id(id, access_token):
import requests
import simplejson
params = {'fields': 'id', 'access_token': access_token}
text = requests.get("https://graph.facebook.com/me", params=params).text
json = simplejson.loads(text)
response_id = json["id"]
return response_id == id
This is the only secure method to verify user token using just one request:
https://graph.facebook.com/debug_token?input_token={token-to-inspect}&access_token={app_id}|{app_secret}
Note that a sign "|" in the above URL isn't used as OR but as separator and must be there after fill the other fields.
The response will be JSON looking like that:
{
data: {
app_id: {app_id},
application: {app_name},
expires_at: {some_number},
is_valid: {true|false}
scopes: {array_of_permissions},
user_id: {user_id}
}
}
Reference: https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
(above method is mentioned at the bottom of this section)
Along with an access token Facebook also sends an "expires_in" parameter, which is an offset value. Use that to compute for when the access token will expire as an NSDate. Then when you need to do a request compare the current date with the expiration date.
Also try to inspect the status codes and response strings Facebook sends back.