So I'm developing my own API for my website - I'm mimicking the Facebook JS SDK in regards to how my system works. API client displays a button, popup comes up and the user can log in. Login popup issues an event to the opener window and the parent window now knows the user is logged in. That all works.
What I'm having trouble understanding is how they can verify that the refresh requests are valid. If the API client needs to send a request to the server to ask if the user is logged in and it's all in JS, then everything is transparent. The API client says, "Hi, I'm application 4jhkk2l3bnm389, is the user that's logged in on Facebook also authenticated with me? If so, can you send me a new token so I can make API calls?" and Facebook says, "Oh, you're application 4jhkk2l3bnm389? Yeah, the user is logged in and has allowed you to access their information, here's an access token."
But how does Facebook prevent an outside application that isn't the authentic application from saying, "Hey, I'M actually application 4jhkk2l3bnm389, I promise I'm not lying. Can I have an access token?"
I have no idea how they determine the difference. Obviously if it was all done through AJAX calls in modern browsers then you could just provide an Access-Control-Allow-Origin header. But if a malicious client were to use cURL then I don't think I could ever tell the difference. How does Facebook do it? A good explanation is much appreciated! Thanks!
All access tokens belongs to an app/user pair, and in order for Facebook to return such an access token to the app, these has to be verified.
The app, or client_id, is verified against the domain specified in the redirect_uri - if the page tries to use a client_id/redirect_uri pair it does not own, then it will not receive the access token as this will be passed to the valid redirect_uri (the mechanism the JS SDK uses follows the same rule).
The user, or uid, is verified using the cookie Facebook sets when you sign in.
While you can easily spoof the client_id/redirect_uri pair using curl, the same does not apply to the uid, as you would have to be in the possession of the users cookie. And if this is the case, well, then you could simply grant your own application access.
Facebook uses OAuth 2.0 for authentication. You can find details of how Facebook deals with OAuth right here: https://developers.facebook.com/docs/authentication/. There are many different ways OAuth can be used, depending on whether you're on a mobile device, a page on facebook.com itself, or, in your case, just a web page outside of facebook.com. The details of that final flow can be found here: https://developers.facebook.com/docs/authentication/client-side/.
Basically, Facebook knows what applications you have given permission to view your information. When you run one of those applications, they first make sure you are logged in to Facebook, then they request a user access token from Facebook, essentially saying, "Hey Facebook, I don't know this person, nor should I. Can I get access to their information?". And then Facebook looks internally and if it decides this particular application should have access to this user's information, it sends a user token.
That's the simple way of describing it. There are many different ways the authentication flow can happen, depending as I said earlier on what kind of device the request is happening from, whether this is a page on facebook.com, etc., essentially based on your security constraints. Best to read the Facebook authentication docs referred to earlier for the details since it can get quite tricky.
Related
I am having a bit of a hard time wrapping my head around how to connect to my OAuth2 Freshbooks API from my bot. Currently I have my API set up such that hitting the /auth route will take the user to the Freshbooks login page and once successfully authenticated the token is returned back to the user. After we have the token the user can get all of their invoices in my web app.
Now, when I build a DialogFlow bot, how do I go about this? What I have thought about is that the user first hits the /auth route which returns the authorization URL which the user can then open in their browser and log in...but after login, how do I return back to my bot?
After a successful login, the /callback route is called by my api with the authorization code to get the token...but this will not be returned back to the bot since it is all happening in the browser...I think.
What is the best approach for this?
Also, after getting the token from my API, should this be stored in a context in my bot?
Thanks for the help and sorry if this is a beginner question. I tried finding an answer online but I just cant wrap my head around this one.
Assuming that your OAuth service is configured correctly you don't have to worry about any of this. The procedure works roughly as follows:
Account linking is triggered via one of two ways:
If you need a linked account to fulfill a certain intent you can simply check the Sign in required box of that intent in the Google Assistant integrations page in your Dialogflow project. If you check this for all intents that are listed for invocation the user can only use your agent once they have an account linked.
The other option is to manually call the sigin helper. This can be done at any point during the conversation, i.e. it does not have to be tied to a particular intent.
When the account linking procedure starts the Google Assistant will load your login page in an in-app browser.
Once the user has authorized your client the OAuth service should (like any OAuth service) redirect the user back to the client. On the Google Assistant this happens via a redirect url of the format https://oauth-redirect.googleusercontent.com/r/<google developer project ID>.
After that Actions on Google calls your fulfillment service with the original intent (the one that triggered account linking), only this time with a valid access token for your service.
Such an access token will from now on be included in every fulfillment request your receive from Actions on Google. You do not have to store this token, you should always use the one that is send in the request.
For more details see the Implement Account Linking documentation.
I'm incorporating Google Sign-In for my website and the basic setup is working fine, however I face a problem to manage the user's login status.
I came across the below SO question, which suggested to use sessionStorage/localStorage to carry the login status across pages of a website, and when user signs out the stored info should be cleared.
https://stackoverflow.com/a/40206395/5345604
I afraid I don't entirely understand the suggested implementation though. What if user signs out from his Google account somewhere else, say from Gmail or from Google Maps? In this case when the user revisits my website, how can I detect that he is no longer signed in with Google and remove the stored client side session? Or this is not the way it is supposed to work?
And on top of the above question, I'm also thinking about the authentication with my backend (PHP). Given the ID token, shall I be sending it across to my server and authenticate it with the Google API Client Library every time? Or shall I only authenticate the user once and store a flag of indication in $_SESSION, and then destroy the session when the user signs out? (Given that the token is in fact a JWT, I suppose the implementation is meant to be stateless and requires the token to be passed to the server every time? Are there any concerns to implement it with the traditional approach of a server side session?)
I can only answer your first question: The login of your website is completely separate from the login status of other Google services. Even YouTube is separate from google.com.
I have a client who would like the most recent post displayed on their site (along with like and comment counts) plus a link to the actual post. Their page is public, so I can view it in a browser without being logged in. Let's just say it's Nike.
http://www.facebook.com/nike (public)
http://graph.facebook.com/nike/feed (wait, i need to authenticate to see this?)
I went through the trouble of setting up a dummy app on a dummy account, got an access_token an was able to pull what I needed using javascript(Jquery). The reason I'm doing it this way is because the client has sensitive data and other apps/sites on this server and does not want to involve their IT department to QA my code.
Before final handoff, they'll likely set up their own facebook app on their account. This would significantly raise the stakes if someone decided they wanted to play with that access_token.
I'm mostly a front-end guy who's done some small php sites/apps, so what would you recommend I do?
Thanks for your time!
I don't know about facebook in particular, but typically if you want to protect api access credentials you would proxy the request through your server, and just send the results to the client (so the access key only exists on the server). Alternately, and I'm not sure that facebook provides this, some APIs will give you a user token, which does not expose your access key, but allows the client to call the api.
02/20/2011:
It was confirmed by Facebook today that indeed there is one call in which the access_token is broadcast in the open . . . it just happens to be one call I use to make sure that the USER is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvase and facebook as a whole. For the most part the Auth and Auth are secure.
Findings:
Subsequent to my posting there was a remark made that this was not really a question but I thought I did indeed postulate one. So that there is no ambiquity here is the question with a lead in:
Since there is no data sent from Facebook during the Canvas Load process that is not at some point divulged, including the access_token, session and other data that could uniquely identify a user, does any one see any other way other than adding one more layer, i.e., a password, sent over the wire via HTTPS along with the access_toekn, that will insure unique untampered with security by the user?
Using Wireshark I captured the local broadcast while loading my Canvas Application page. I was hugely surprised to see the access_token broadcast in the open, viewable for any one to see. This access_token is appended to any https call to the Facebook OpenGraph API.
Using facebook as a single click log on has now raised huge concerns for me. It is stored in a session object in memory and the cookie is cleared upon app termination and after reviewing the FB.Init calls I saw a lot of HTTPS calls so I assumed the access_token was always encrypted.
But last night I saw in the status bar a call from what was simply an http call that included the App ID so I felt I should sniff the Application Canvas load sequence.
Today I did sniff the broadcast and in the attached image you can see that there are http calls with the access_token being broadcast in the open and clear for anyone to gain access to.
Am I missing something, is what I am seeing and my interpretation really correct. If any one can sniff and get the access_token they can theorically make calls to the Graph API via https, even though the call back would still need to be the site established in Facebook's application set up.
But what is truly a security threat is anyone using the access_token for access to their own site. I do not see the value of a single sign on via Facebook if the only thing that was established as secure was the access_token - becuase for what I can see it clearly is not secure. Access tokens that never have an expire date do not change. Access_tokens are different for every user, to access to another site could be held tight to just a single user, but compromising even a single user's data is unacceptable.
http://www.creatingstory.com/images/InTheOpen.png
Went back and did more research on this:
FINDINGS:
Went back an re ran the canvas application to verify that it was not any of my code that was not broadcasting.
In this call: HTTP GET /connect.php/en_US/js/CacheData HTTP/1.1
The USER ID is clearly visible in the cookie. So USER_ID's are fully visible, but they are already. Anyone can go to pretty much any ones page and hover over the image and see the USER ID. So no big threat. APP_ID are also easily obtainable - but . . .
http://www.creatingstory.com/images/InTheOpen2.png
The above file clearly shows the FULL ACCESS TOKEN clearly in the OPEN via a Facebook initiated call.
Am I wrong. TELL ME I AM WRONG because I want to be wrong about this.
I have since reset my app secret so I am showing the real sniff of the Canvas Page being loaded.
Additional data 02/20/2011:
#ifaour - I appreciate the time you took to compile your response.
I am pretty familiar with the OAuth process and have a pretty solid understanding of the signed_request unpacking and utilization of the access_token. I perform a substantial amount of my processing on the server and my Facebook server side flows are all complete and function without any flaw that I know of. The application secret is secure and never passed to the front end application and is also changed regularly. I am being as fanatical about security as I can be, knowing there is so much I don’t know that could come back and bite me.
Two huge access_token issues:
The issues concern the possible utilization of the access_token from the USER AGENT (browser). During the FB.INIT() process of the Facebook JavaScript SDK, a cookie is created as well as an object in memory called a session object. This object, along with the cookie contain the access_token, session, a secret, and uid and status of the connection. The session object is structured such that is supports both the new OAuth and the legacy flows. With OAuth, the access_token and status are pretty much al that is used in the session object.
The first issue is that the access_token is used to make HTTPS calls to the GRAPH API. If you had the access_token, you could do this from any browser:
https://graph.facebook.com/220439?access_token=...
and it will return a ton of information about the user. So any one with the access token can gain access to a Facebook account. You can also make additional calls to any info the user has granted access to the application tied to the access_token. At first I thought that a call into the GRAPH had to have a Callback to the URL established in the App Setup, but I tested it as mentioned below and it will return info back right into the browser. Adding that callback feature would be a good idea I think, tightens things up a bit.
The second issue is utilization of some unique private secured data that identifies the user to the third party data base, i.e., like in my case, I would use a single sign on to populate user information into my database using this unique secured data item (i.e., access_token which contains the APP ID, the USER ID, and a hashed with secret sequence). None of this is a problem on the server side. You get a signed_request, you unpack it with secret, make HTTPS calls, get HTTPS responses back. When a user has information entered via the USER AGENT(browser) that must be stored via a POST, this unique secured data element would be sent via HTTPS such that they are validated prior to data base insertion.
However, If there is NO secured piece of unique data that is supplied via the single sign on process, then there is no way to guarantee unauthorized access. The access_token is the one piece of data that is utilized by Facebook to make the HTTPS calls into the GRAPH API. it is considered unique in regards to BOTH the USER and the APPLICATION and is initially secure via the signed_request packaging. If however, it is subsequently transmitted in the clear and if I can sniff the wire and obtain the access_token, then I can pretend to be the application and gain the information they have authorized the application to see. I tried the above example from a Safari and IE browser and it returned all of my information to me in the browser.
In conclusion, the access_token is part of the signed_request and that is how the application initially obtains it. After OAuth authentication and authorization, i.e., the USER has logged into Facebook and then runs your app, the access_token is stored as mentioned above and I have sniffed it such that I see it stored in a Cookie that is transmitted over the wire, resulting in there being NO UNIQUE SECURED IDENTIFIABLE piece of information that can be used to support interaction with the database, or in other words, unless there were one more piece of secure data sent along with the access_token to my database, i.e., a password, I would not be able to discern if it is a legitimate call. Luckily I utilized secure AJAX via POST and the call has to come from the same domain, but I am sure there is a way to hijack that.
I am totally open to any ideas on this topic on how to uniquely identify my USERS other than adding another layer (password) via this single sign on process or if someone would just share with me that I read and analyzed my data incorrectly and that the access_token is always secure over the wire.
Mahalo nui loa in advance.
I am not terribly familiar with Facebook's authentication/authorization methods, but I do believe that they implement oauth (or something close to it) for delegation, distributed authorization, and "single sign-on".
OAuth is described by RFC-5849
EDIT: Facebook Uses OAuth 2.0 which is still in working draft.
In OAuth, and similar systems, the "access_token" is only part of the picture. There is also typically a secret key, which is known only by the service provider (facebook) and the client application (your app). The secret key is the only part that is expected to stay secret - and that part is never sent over the wire (after it's initial issuance).
In the case of Facebook, I think the secret key is assigned to you when you register your application to use their API, and the 'access_token' is returned to you for a given user, whenever the user agrees to allow your app to access their info.
Messages are sent in the clear, including the user's username, and the relevant "access_token"; However, each message must also include a valid signature in order to be accepted by the server. The signature is a cryptographically computed string, that is created using a technique called HMAC.
Computing the HMAC signature requires both the token and the secret, and includes other key parts of the message as well. Each signature is unique for the given message contents; and each message uses a nonce to ensure that no two messages can ever be exactly identical.
When the server receives a signed message, it starts by extracting the access_token (clear-text), and determining which app the token was issued for. It then retrieves the matching secret from its own local database (the secret is not contained in the message). Finally, the server uses the clear-text message, the clear-text access_token, and the secret to compute the expected HMAC signature for the message. If the computed signature matches the signature on the received message, then the message must have been sent by someone who knows the same secret (i.e. your application).
Have a look at Section 3.1 of RFC-5849 for an OAuth specific example, and further elaboration on the details.
Incidentally, the same approach is used by Amazon to control access to S3 and EC2, as well as most other service providers that offer API access with long-term authorization. Suffice it to say - this approach is secure. It might be a little counter-intuitive at first, but it makes sense once you think it through.
Adding a few links and quotes from Facebook Documentation:
Facebook is indeed using the HMAC-SHA256 algorithm. Registration document (PHP Example reading signed_request section).
Always verify the signed_request:
If you are unable to validate the
signed_request because you can't embed
your application secret (e.g. in
javascript or a desktop application)
then you MUST only use one piece of
information from the payload, the
oauth_token.
The Authentication Document contains a lot of useful info about the different flows you may use to authenticate a user. Also read the Security Considerations section at the bottom of the page:
Cross site request forgery is an
attack in which an trusted
(authenticated and authorized) user
unknowingly performs an action on
website. To prevent this attack, you
should pass an identifier in the state
parameter, and then validate the state
parameter matches on the response. We
strongly recommend that any app
implementing Facebook user login
implement CSRF protection using this
mechanism.
It was confirmed by Facebook that indeed there is one call in which the access_token is broadcast in the open - it just happens to be one call I use to make sure that the user is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvas and Facebook as a whole. For the most part the Auth and Auth are secure.
To ensure a secure interface between a third party application and a Facebook application or even any website that uses Facebook Single Sign on, an identity question would provide the extra layer when used in conjunction with the access_token.
Either that or require your users to use Facebook with the new SSL feature of Facebook and Facebook Canvas Applications. If the access_token is broadcast in the open it cannot be used to uniquely identify anyone in your third party database when needing to have a confirmed identity before database interactions.
According to the Facebook API documentation, most of the work is handled through javascript.
That means that all the processing is done, and then the front end checks if the user is connected to Facebook/authorized. right?
My question is:
Suppose a user goes to my site for the first time ever.
He clicks on "facebook connect". The javascript verifies him as authentic, and it "redirects" to another page on my server. From then on, how do I know that the user is actually authenticated to my website, since everything is done on frontend?
I think this is correct, but aren't there some security issues..:
-After user clicks Login, Facebook redirects to a page on my site. AND they also create a cookie with a specific "Facebook ID" that is retrieved only from this user. My backened will "read" the cookie and grab that ID...and then associate it to my userID.
If that is correct...then it doesn't make sense. What if people steal other people's "facebook ID" and then forge the cookie? And then my backend sees the cookie and thinks it's the real user...?
Am I confused? If I am confused, please help me re-organize and tell me how it's like.
Facebook Connect uses a clever (or insane, depending on your point of view) hack to achieve cross-site communication between your site and Facebook's authentication system from within the browser.
The way it works is as follows:
Your site includes a very simple static HTML file, known as the cross-domain communications channel. This file is called xd_receiver.htm in the FB docs, but it can be named anything you like.
Your site's login page includes a reference to the Javascript library hosted on Facebook's server.
When a user logs in via the "Connect" button, it calls a function in Facebook's JS API which pops up a login dialog. This login box has an invisible iframe in which the cross-domain communications file is loaded.
The user fills out the form and submits it, posting the form to Facebook.
Facebook checks the login. If it's successful, it communicates this to your site. Here's where that cross-domain stuff comes in:
Because of cross-domain security policies, Facebook's login window can not inspect the DOM tree for documents hosted on your server. But the login window can update the src element of any iframe within it, and this is used to communicate with the cross-domain communications file hosted on your page.
When the cross-domain communications file receives a communication indicating that the login was successful, it uses Javascript to set some cookies containing the user's ID and session. Since this file lives on your server, those cookies have your domain and your backend can receive them.
Any further communication in Facebook's direction can be accomplished by inserting another nested iframe in the other iframe -- this second-level iframe lives on Facebook's server instead of yours.
The cookies are secure (in theory) because the data is signed with the secret key that Facebook generated for you when you signed up for the developer program. The JS library uses your public key (the "API key") to validate the cookies.
Theoretically, Facebook's Javascript library handles this all automatically once you've set everything up. In practice, I've found it doesn't always work exactly smoothly.
For a more detailed explanation of the mechanics of cross-domain communication using iframes, see this article from MSDN.
Please someone correct me if I'm wrong - as I am also trying to figure all this stuff out myself. My understanding with the security of the cookies is that there is also a cookie which is a special signature cookie. This cookie is created by combining the data of the other cookies, adding your application secret that only you and FB know, and the result MD5-Hashed. You can then test this hash server-side, which could not easily be duplicated by a hacker, to make sure the data can be trusted as coming from FB.
A more charming explaination can be found here - scroll about halfway down the page.
Same issues here, and I think Scott is closer to the solution.
Also Im using "http://developers.facebook.com/docs/?u=facebook.jslib-alpha.FB.init" there open source js framework. So things are a little different.
For me, via the opensource js framework, facebook provides and sets a session on my site with a signature. So what I am thinking is to recreate that signature on my side. - if they both match then the user is who he says he is.
So basically if a user wanted to save something to my database, grab the session signature set up by facebook and recreate that signature with php and validate it against the one facebook gave me?
if($_SESSION['facebookSignature'] == reGeneratedSignature){
// save to database
}else{
// go away I don't trust you
}
But how do you regenerate that signature? preferably without making more calls to Facebook?