Facebook login architecture - facebook

I'm developing an App which uses Facebook to authenticate. So far, what I have is the following:
Client->FB: authentication data (fb user & password),
FB->Client: FB id, FB token,...
Client->AppServer: FB data (id, token,..)
AppServer->Client: OK/Not OK, whatever necessary data.
Where:
Client: The App installed in the user's device (in this case, iOS),
FB: FB server (the client talks to it through FB's SDK),
AppServer: My App's server.
The point of all these is to validate if the user is real. The problem is that in #3 the Client could be sending a random FB id or FB token to the AppServer, so the question is: is there anyway to check in the AppServer whether this FB data is indeed real?

This worked for me:
Given a FB_CLIENT_TOKEN and FB_CLIENT_ID coming from a client, and knowing your FB_APP_ID and FB_APP_SECRET, we want to validate in our server if the fb account from the client is real. Then:
From server, access https://graph.facebook.com/oauth/access_token?client_id=FB_APP_ID&client_secret=FB_APP_SECRET&grant_type=client_credentials
This will return a plaintext with access_token=APP_TOKEN.
Next, access https://graph.facebook.com/debug_token?input_token=FB_CLIENT_TOKEN&access_token=APP_TOKEN
This will return a JSON with a field named user_id, so the last step is to check if this field equals FB_CLIENT_ID.

Related

Facebook OAuth security using passport-facebook

I am currently using a client-side React component to have a user login to Facebook via OAuth in my application. On the server-side, I use the npm package passport-facebook-token to validate the authenticity of the accessToken after a successful client-side login.
One practice I do not see often is in addition to asking Facebook if the accessToken is valid, shouldn't the server also check if the email provided by the client's payload matches the e-mail coming back from Facebook? Allow me to use defined client/server technologies to illustrate my question:
1) User uses React component on the client to authenticate with Facebook.
2) React component successfully authenticates with Facebook and fires an HTTP request to the server with an access token and the user's email.
3) The server, running Node.JS and passport-facebook, now needs to verify the authenticity of the access token directly from Facebook. Facebook does not care for an e-mail. It will just verify the access token.
4) Facebook returns a response to Node.js confirming the authenticity of the access token. The response also contains other metadata about the user, including their email and other profile data.
My question is, should Node.js take the email that's also coming back from Facebook's access token verification payload, and verify that it is what came back from the React client? Would this not prevent someone from brute-forcing an accessToken and require them to not only have an accessToken but also know who the accessToken belongs to? This could prevent a user from submitting a bunch of HTTP POST requests to the Node.js server attempting different access tokens. They would not only have to guess an access token assigned to the application's clientID, but also know the e-mail it belongs to. Is this an over-engineered approach?
Really the best way I can think of to make your OAuth accessToken and 'code' value less prone to brute-forcing is using a Cryptographic Number Generator to create a 128-bit length string of random data and encoding it with base 64 to use as your code. It's extremely unlikely that it would be guessed by a computer or by someone redirecting to and from the authorization endpoint and the redirect-uri with query parameters.
Another method of fortification is limiting the rate of authorizations by IP address (which you can do instead of email through Node.js) but that is usually not a problem for most well-equipped hackers. I highly advise the first method for creating a more secure service.
Your approach to validate the email as well as the token is a bit superfluous because Facebook's opaque user access tokens are inherently tied to email.
From Facebook
An access token is an opaque string that identifies a user, app, or Page
"opaque" is defined by Auth0 here
Opaque Access Tokens are tokens in a proprietary format that typically contain some identifier to information in a server’s persistent storage
In your case, the identifier is the user's email, and the server belongs to Facebook.
I will elaborate further. Here is your step by step with some edits:
User uses React component on the client to authenticate with Facebook, inputting both their email and password directly to Facebook. React component gets the token from Facebook on login success.
React component successfully authenticates with Facebook and fires an HTTP request to the server with an access token and the user's email.
The server, running Node.JS and passport-facebook, now needs to verify the authenticity of the access token directly from Facebook. Facebook does not care for an e-mail. It will just verify the access token because the access token is already tied to the email.
Facebook returns a response to Node.js confirming the authenticity of the access token. The response also contains other metadata about the user, including their email and other profile data.
This is Facebook's bug bounty program. If their OAuth was really as cracked as to require a second email validation, it would have been patched almost immediately by this incentive.

Facebook GraphQL - How to get a users email address and name from backend

I have an app where the user logs into Facebook (and thus has an Auth Token) and then sends that token to my server for authentication within the app.
If it's the users first time in the app, I need to sign them up as well (gather email and name)
Using the users FB auth token (and any server-side tokens) how do I retrieve the user's email address and name? (What endpoints do I need to hit with what tokens/body?)
--
Additional Info:
The login is scoped with ['public_profile', 'email']
The application is running in Node.js on AWS Lambda, and I'd prefer to make a simple fetch if possible instead of installing a whole gql client.
I have tried looking at their graphQL documentation, but I can't
seem to make heads or tails out of it.
I do have access to the user's ID (example: 10157426730xxxxxx)
This would be the API call to get the name and email of a user, with a User Token:
https://graph.facebook.com/me?fields=name,email&access_token=xxxx
Alternatively, you can add the version:
https://graph.facebook.com/v4.0/me?fields=name,email&access_token=xxxx
All the existing fields for users are here to find: https://developers.facebook.com/docs/graph-api/reference/user/
You do not need the User ID, the User Token identifies the User anyway and you can just use "me" instead of the ID. The Graph API is a REST API though, not GraphQL.

OAuth - what to store on disk

TL;DR When using google oauth on desktop app, what to save on disk to avoid repeated sign in? Save the google user id? or the token? or an session id?
I'm creating an little desktop app, whitch must authenticate to my REST API server. I'm using google oauth2 for that.
The idea is, that when the desktop app will be authentivated, it generates some data that will be send to my server. The server will store the data with the google user id received from https://www.googleapis.com/userinfo/v2/me.
On the first run of the desktop app, it will open the default browser, with and url for my server and start an local http server. then:
my server will redirect the browser to google (with the clientid, secret, etc.)
user logs in and it will be redirected back to the server with the oauth code
server uses the code to get the token, and then the user profile and stores the token and the profile in db, then redirects the browser to localhost with an paramerer
the desktop app catches the parameter and stores it in an file on the disk
next time the desktop app will start it only reads the file for the parameter to send the generated data with it to my server
my question is: what the parameter should be? the google user id? the oauth token? an generated session id for this desktop app? or something else?
when it will be the google user id, it can conveniently sent the data with the user id and the rest server will just store it in db as is. but I don't think it's safe
when it will be the token, the rest server has to with every request also get the user profile from google with the token. and imho sending the token with every request isn't safe either
generating an session id means to store it with the user and the token on the server and the desktop app will just store it and send it with every request. but I don't know if it's safe to do that
As it's normally the case in software development you have a couple of options depending on requirements.
The mandatory requirement is that your client (desktop) application needs to send something to your REST API so that the API can perform up to two decisions:
Decide who the user is.
Decide if the user is authorized to perform the currently requested action.
The second step may not be applicable if all authenticated users have access to exactly the same set of actions so I'll cover both scenarios.
Also note that, for the first step, sending the Google user ID is not a valid option as that information can be obtained by other parties and does not ensure that the user did authenticate to use your application.
Option 1 - Authentication without fine-grained authorization
Either always sending the id_token or exchanging that token with your custom session identifier both meet the previous requirement, because the id_token contains an audience that clearly indicates the user authenticated to use your application and the session identifier is generated by your application so it can also ensure that. The requests to your API need to use HTTPS, otherwise it will be too easy for the token or session ID to be captured by an attacker.
If you go with the id_token alternative you need to take in consideration that the token will expire; for this, a few options again:
repeat the authentication process another time; if the user still has a session it will indeed be quicker, but you still have to open a browser, local server and repeat the whole steps.
request offline_access when doing the first authentication.
With the last option you should get a refresh token that would allow for your application to have a way to identify the user even after the first id_token expires. I say should, because Google seems to do things a bit different than the specification, for example, the way to obtain the refresh token is by providing access_type=offline instead of the offline_access from OpenID Connect.
Personally, I would go with the session identifier as you'll have more control over lifetime and it may also be simpler.
Option 2 - Authentication + fine-grained authorization
If you need a fine-grained authorization system for your REST API then the best approach would be to authenticate your users with Google, but then have an OAuth 2.0 compliant authorization server that would issue access tokens specific for your API.
For the authorization server implementation, you could either:
Implement it yourself or leverage open source components
⤷ may be time consuming, complex and mitigation of security risks would all fall on you
Use a third-party OAuth 2.0 as a servive authorization provider like Auth0
⤷ easy to get started, depending on amount of usage (the free plan on Auth0 goes up to 7000 users) it will cost you money instead of time
Disclosure: I work at Auth0.
There should be no problem sending the access_token with every request since they are created for that purpose and are thus short lived. You can use the Google Authorization Server endpoint to verify a token instead of using it to do a request for a users profile.
If you're only relying on Google for authentication, here's how your workflow can look:
the client (desktop application, in your case) retrieves the
Google id_token following the user's log in, and then sends it to
the server
the server validates the integrity of said token and extracts the user's profile data; this could mean a simple GET on Google's endpoint to verify this token: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
On subsequent requests, nothing should change really, except that the user's login process will be automated (since he's given permissions & all), and thus much faster. #danielx is right, there's no problem with sending the token each and every time.

Is it possible to use google as authentication server only without using any API and how

I have a C# desktop application that works against a server(play framework 2.5 if it matters)with REST API calls.
I want to authenticate the calls from the client(desktop app) to the server using gmail accounts.
now before displaying the flow, some clarifications:
the client app is sometimes unattended(I receive the username+password from somewhere else) - meaning displaying a user consent screen is not an option
I have no interest in any Google APIs! I only want to access my server REST API.
I only need Google for the authentication stage
the wanted flow:
In the client:
get the username and password from the user(as mentioned can be done non interactively)
authenticate the user against google and get a token
send a request to the server including the token
In the server:
"decode" the token back into username
check if this user is authenticated to do the call and act accordingly
if possible - how would you do it?
You'll first need to setup a new project on the Google API Manager (that's where that content Guid is coming from below in the meta tag).
On the client side, here's how you can use Google's Sign-In button template to initialize the login & grant of permissions process:
<meta name="google-signin-client_id" content="29chqvu3ghgtte1.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
<div id="google-signin-button"
class="g-signin2"
data-width="170"
data-height="30"
data-onsuccess="onSignIn"
data-onfailure="onSignInFailure">
</div>
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var idToken = googleUser.getAuthResponse().id_token;
}
Then you can simply send this idToken to the server/api, from where you can do a GET on Google's endpoint to verify this token https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}. There are of course numerous other ways in which you can validate the token (and actually get a handle on it, of course).
This would be the gist of it (excluding the non-interactive user login - keep in mind you'll also need the user to grant permissions).

Verifying a Facebook Connect session

I am writing an application which has multiple frontend clients using Facebook Connect as a single signon solution.
One of these clients, for example is running on the iOS platform, so all fb authentication happens on the client via the iOS Facebook SDK. After a user authenticates on the client, however, since I am using Facebook for signing in to our site, is there anyway for the server to take the auth data from the client, and verify it?
Using the Javascript SDK, call FB.getSession() to get a JS object with various auth-related bits. Of these, pass 'access_token', 'expires', 'secret', 'session_key', 'uid' and 'sig' along to your server.
On server-side to check if the call is authentic, concat these into a string such as "access_token=1290380d9e&expires=92d903&secret=e9cj9kd&session_key=s49i9i3f&uid=12345". So everything you just passed, EXCLUDING the sig.
To this string, concat the "app secret" which you can get from Facebook developer site. Take the MD5 hexdigest (in Python import md5;md5.new(foobar).hexdigest()) of the string and compare to see if it is the same as the 'sig' you passed.