In order to improve app quality i'm working on testing: unit tests and UI tests. Since i'm having Dropbox support in the app i'd like to test it and i need to auth to Dropbox account before testing (in my android app the users are able to save the files, read them, rename, etc - basic file routines).
Dropbox provides Java/Android SDK v2 with examples but even command-line tool requires some manual actions - open browser app with the URL and select account:
// Run through Dropbox API authorization process
DbxRequestConfig requestConfig = new DbxRequestConfig("examples-authorize");
DbxWebAuth webAuth = new DbxWebAuth(requestConfig, appInfo);
DbxWebAuth.Request webAuthRequest = DbxWebAuth.newRequestBuilder()
.withNoRedirect()
.build();
String authorizeUrl = webAuth.authorize(webAuthRequest);
System.out.println("1. Go to " + authorizeUrl);
System.out.println("2. Click \"Allow\" (you might have to log in first).");
System.out.println("3. Copy the authorization code.");
System.out.print("Enter the authorization code here: ");
String code = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (code == null) {
System.exit(1); return;
}
code = code.trim();
DbxAuthFinish authFinish;
try {
authFinish = webAuth.finishFromCode(code);
} catch (DbxException ex) {
System.err.println("Error in DbxWebAuth.authorize: " + ex.getMessage());
System.exit(1); return;
}
System.out.println("Authorization complete.");
System.out.println("- User ID: " + authFinish.getUserId());
System.out.println("- Access Token: " + authFinish.getAccessToken());
Any possibility to make Dropbox auth automatically without manual interaction? I expect to provide app key/secret, account email/password and get accessToken for the session.
PS. I'd like to avoid using Robelectric+Espresso and keep it in unit/integration tests, not in UI tests.
No, the Dropbox API doesn't offer a way to automate the app authorization flow.
Note that you can store and re-use access tokens though, so you may want to just get one for your test account once manually, and then re-use that.
Here is my test template (i hope it helps somebody).
You have to do TODO and run the test manually twice at least (to paste auth code and access token) and then you can do the testing. All the next tests invocations do not require to do anything manually.
public class DropboxFileSystemTest {
// credentials
private static final String APP_KEY = ""; // TODO : paste your app key
private static final String APP_SECRET = ""; // TODO : paste your app secret
// do run the test and follow the instructions
private static final String accountEmail = "test#domain.com"; // TODO : paste your test Dropbox account
private static String authCode; // TODO : run the test and paste auth code
private static String accessToken // TODO : run the test and paste access
private DbxAppInfo appInfo = new DbxAppInfo(APP_KEY, APP_SECRET);
// Run through Dropbox API authorization process
private DbxRequestConfig requestConfig = new DbxRequestConfig(DropboxFileSystemTest.class.getSimpleName());
private DbxWebAuth webAuth = new DbxWebAuth(requestConfig, appInfo);
private void startAuth() throws IOException {
DbxWebAuth.Request webAuthRequest = DbxWebAuth.newRequestBuilder()
.withNoRedirect()
.build();
String authorizeUrl = webAuth.authorize(webAuthRequest);
System.out.println("1. Go to " + authorizeUrl);
System.out.println("2. Click \"Allow\" (you might have to log in first). WARNING: log-in to " + accountEmail);
System.out.println("3. Copy the authorization code.");
System.out.println("4. Paste the authorization code to this test `this.authCode` value");
System.out.println("5. Re-run the test");
}
private DbxClientV2 client; // to be used for the requests
private void initWithAccessToken() {
DbxRequestConfig config = new DbxRequestConfig(UUID.randomUUID().toString());
client = new DbxClientV2(config, accessToken);
}
private void initAndVerifyAccount() throws DbxException {
initWithAccessToken();
// check expected account (trying to prevent user account to be wiped out)
DbxClientV2 client = DbxClientHolder.get().getClient();
FullAccount account = client.users().getCurrentAccount();
if (!account.getEmail().equals(accountEmail))
throw new RuntimeException("Wrong account: current is " + account.getEmail() + ", but " + accountEmail + " is expected");
}
private void clearFileSystem() throws FileSystemException {
// TODO : clear Dropbox file system
}
#Before
public void setUp() throws IOException, FileSystemException, DbxException {
auth();
clearFileSystem();
}
private void finishAuth() {
DbxAuthFinish authFinish;
try {
authFinish = webAuth.finishFromCode(authCode);
} catch (DbxException ex) {
System.err.println("Error in DbxWebAuth.authorize: " + ex.getMessage());
System.exit(1); return;
}
System.out.println("Authorization complete.");
System.out.println("- User ID: " + authFinish.getUserId());
System.out.println("- Access Token: " + authFinish.getAccessToken());
System.out.println();
System.out.println("1. Copy the access token");
System.out.println("2. Paste the access token to this test `this.accessToken` value");
System.out.println("3. Re-run the test");
accessToken = authFinish.getAccessToken();
}
private void auth() throws IOException, FileSystemException, DbxException {
if (accessToken == null) {
if (authCode != null ) {
finishAuth();
throw new RuntimeException("Manual actions required: copy-paste access token");
} else {
startAuth();
throw new RuntimeException("Manual actions required: copy-paste authCode");
}
} else {
initAndVerifyAccount();
}
}
#After
public void tearDown() throws FileSystemException {
if (client != null) {
clearFileSystem();
}
}
#Test
public void testSmth() {
// TODO : write your test using `this.client` for requests
}
Related
I am working on a Java app that needs to get data from our SAP system laying in SAP table. I try to connect the SW with SAP using SAP Java Connector 3.X, but I'm having trouble with the Destination.
I used the Code-Examples that came with the SAP Java Connector.
public class CustomDestinationDataProvider {
static class MyDestinationDataProvider implements DestinationDataProvider {
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String ABAP_AS) {
try {
//read the destination from DB
Properties p = secureDBStorage.get(ABAP_AS);
if(p!=null) {
//check if all is correct, for example
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
} catch(RuntimeException re) {
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
//An implementation supporting events has to retain the eventListener instance provided
//by the JCo runtime. This listener instance shall be used to notify the JCo runtime
//about all changes in destination configurations.
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
this.eL = eventListener;
}
public boolean supportsEvents() {
return true;
}
//implementation that saves the properties in a very secure way
void changeProperties(String ABAP_AS, Properties properties) {
synchronized(secureDBStorage) {
if(properties==null) {
if(secureDBStorage.remove(ABAP_AS)!=null)
eL.deleted(ABAP_AS);
} else {
secureDBStorage.put(ABAP_AS, properties);
eL.updated(ABAP_AS); // create or updated
}
}
}
} // end of MyDestinationDataProvider
//business logic
void executeCalls(String ABAP_AS) {
JCoDestination dest;
try {
dest = JCoDestinationManager.getDestination(ABAP_AS);
dest.ping();
System.out.println("Destination " + ABAP_AS + " works");
} catch(JCoException e) {
e.printStackTrace();
System.out.println("Execution on destination " + ABAP_AS + " failed");
}
}
static Properties getDestinationPropertiesFromUI() {
//adapt parameters in order to configure a valid destination
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "XX");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "XX");
createDestinationDataFile(ABAP_AS, connectProperties);
return connectProperties;
}
static void createDestinationDataFile(String ABAP_AS, Properties connectProperties) {
File destCfg = new File(ABAP_AS + ".jcoDestination");
try {
FileOutputStream fos = new FileOutputStream(destCfg, false);
connectProperties.store(fos, "for tests only!");
fos.close();
} catch (Exception e) {
throw new RuntimeException("Unable to create the destination files", e);
}
}
}
This is the Error-Message I get from NetBeans:
Destination ABAP_AS_WITHOUT_POOL works
Execution on destination ABAP_AS_WITHOUT_POOL failed
com.sap.conn.jco.JCoException: (106) JCO_ERROR_RESOURCE: Destination ABAP_AS_WITHOUT_POOL does not exist
at com.sap.conn.jco.rt.DefaultDestinationManager.update(DefaultDestinationManager.java:217)
at com.sap.conn.jco.rt.DefaultDestinationManager.searchDestination(DefaultDestinationManager.java:382)
at com.sap.conn.jco.rt.DefaultDestinationManager.getDestinationInstance(DefaultDestinationManager.java:100)
at com.sap.conn.jco.JCoDestinationManager.getDestination(JCoDestinationManager.java:104)
at jcotest2.CustomDestinationDataProvider.executeCalls(CustomDestinationDataProvider.java:92)
at jcotest2.Main.main(Main.java:39)
BUILD SUCCESSFUL (total time: 2 seconds)
It looks like your code keeps the logon data in the HashMap "secureDBStorage". Where do you fill this HashMap?
Also: what do you need the method "createDestinationDataFile()" for, if you are using a HashMap and not a file?
Edit: as this post got deleted by review, I'm trying to make it more precise now. So there are two problems with your code:
You keep the logon parameters of your backend systems in a HashMap named "secureDBStorage", but you did not fill this map with parameters for the system/destination named "ABAP_AS_WITHOUT_POOL".
Your code still contains the method "createDestinationDataFile()", which was probably copied from the sample program and then forgotten to delete. As your program is using a HashMap for storing the logon parameters and not the file system, you can delete this method. (Only confuses the program.)
Prior to calling the destination you should create it in Configuration Manager of SAP Java Application Server (NWA Manager) and then only call it.
https://help.sap.com/doc/saphelp_nwpi711/7.1.1/enUS/07/0d27932264284b883dab13ce1008a6/frameset.htm
Here is the sample:
static String ABAP_AS = "WD_MODELDATA_DEST";
PrintWriter out = response.getWriter();
JCoDestination destination;
try {
destination = JCoDestinationManager.getDestination(ABAP_AS);
out.println("Attributes:");
out.println(destination.getAttributes());
out.println();
} catch (JCoException e) {
e.printStackTrace();
out.println(e.getMessage());
}
I don't see where you fill ABAP_AS variable in your code like in the sample.
In my android application, I'm integrating the Dialogflow V2 Agent. There's no specific SDK for Android yet. So I'm using the java client library from Dialogflow. followed the tutorial https://github.com/dialogflow/dialogflow-java-client-v2/issues/25.
I've added the dependencies(dialogflow & oauth2) and created a service account in the google-cloud console. Added the credential file into raw folder. followed the tutorial here https://github.com/dialogflow/dialogflow-java-client-v2/issues/25. Getting the error as
java.lang.NoSuchMethodError: No static method
decodeBase64(Ljava/lang/String;)[B in class
Lorg/apache/commons/codec/binary/Base64; or its super classes
(declaration of 'org.apache.commons.codec.binary.Base64' appears in
/system/framework/org.apache.http.legacy.boot.jar)
private void createDialogflow() {
try {
InputStream stream = getResources().openRawResource(R.raw.dialogflow_service_credentials);
GoogleCredentials credentials = GoogleCredentials.fromStream(stream);
String projectId = ((ServiceAccountCredentials) credentials).getProjectId();
SessionsSettings.Builder settingsBuilder = SessionsSettings.newBuilder();
SessionsSettings sessionsSettings = settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)).build();
sessionsClient = SessionsClient.create(sessionsSettings);
String uuid = UUID.randomUUID().toString();
session = SessionName.of(projectId, uuid);
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMessage(String msg) {
// Java V2
setTypingMessage();
QueryInput queryInput = QueryInput.newBuilder().setText(TextInput.newBuilder().setText(msg).setLanguageCode("en-US")).build();
new RequestJavaV2Task(mContext, session, sessionsClient, queryInput).execute();
}
public void callbackV2(DetectIntentResponse response) {
removeTyingMessage();
if (response != null) {
// process aiResponse here
String botReply = response.getQueryResult().getFulfillmentText();
Log.d("botReply", "V2 Bot Reply: " + botReply);
setBotMessage(botReply);
} else {
Log.d("botReply", "Bot Reply: Null");
setBotMessage("There was some communication issue. Please Try again!");
}
}
Is there any clear documentations on how to integrate Dialogflow v2 into my android application.
The tutorial you were following was updated. In case anyone is facing this issue, according to the repo:
Save your CLIENT_ACCESS_TOKEN in gradle.properties
wish to know if the credential file generated by Java's kinit is the same as that created in "cache" or memory when the user logged in.
For example, using kinit the credential file is by default krb5_[username].
I also have a piece of code that gets the Kerberos ticket from the currently logged in user:
public static byte[] getTicket() {
// defined the realm, kdc etc here
.....
try {
LoginContext lc = new LoginContext("SomeLoginContext");
lc.login();
Subject signedOnUserSubject = lc.getSubject();
Set<Object> privatePrincipalsSet = signedOnUserSubject.getPrivateCredentials();
if (privatePrincipalsSet != null && privatePrincipalsSet.size() > 0) {
for (Object privatePrincipal : privatePrincipalsSet) {
//make sure it is the kerberos ticket
if (privatePrincipal instanceof KerberosTicket) {
KerberosTicket ticket = (KerberosTicket)privatePrincipal;
return ticket.getEncoded(); <------- is this correct?
}
}
}
}
catch (LoginException e) {
e.printStackTrace();
} finally {
}
return null;
}
May I know if the internal "format" of the credentials are the same.?
thanks
I have a written a program that takes in user input, but now I want to be able to save that input by editing a Google spreadsheet every time a user submits the form. So basically, the Google spreadsheet is constantly being updated.
Can anyone provide a tutorial on how I might be able to achieve this? I'm writing in Java using Eclipse, so which plug-ins would I need?
I have already tried using some of the sample code provided in the Google Spreadsheets API (adding a list row section), but I can't seem to get it to work.
import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;
import java.io.IOException;
import java.net.*;
import java.util.*;
public class MySpreadsheetIntegration {
public static void main(String[] args)
throws AuthenticationException, MalformedURLException, IOException, ServiceException {
SpreadsheetService service =
new SpreadsheetService("MySpreadsheetIntegration-v1");
// TODO: Authorize the service object for a specific user (see other sections)
// Define the URL to request. This should never change.
URL SPREADSHEET_FEED_URL = new URL(
"https://docs.google.com/spreadsheets/d/1OcDp1IZ4iuvyhndtrZ3OOMHZNSEt7XTaaTrhEkNPnN4/edit#gid=0");
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL,
SpreadsheetFeed.class);
List<SpreadsheetEntry> spreadsheets = feed.getEntries();
if (spreadsheets.size() == 0) {
// TODO: There were no spreadsheets, act accordingly.
}
// TODO: Choose a spreadsheet more intelligently based on your
// app's needs.
SpreadsheetEntry spreadsheet = spreadsheets.get(0);
System.out.println(spreadsheet.getTitle().getPlainText());
// Get the first worksheet of the first spreadsheet.
// TODO: Choose a worksheet more intelligently based on your
// app's needs.
WorksheetFeed worksheetFeed = service.getFeed(
spreadsheet.getWorksheetFeedUrl(), WorksheetFeed.class);
List<WorksheetEntry> worksheets = worksheetFeed.getEntries();
WorksheetEntry worksheet = worksheets.get(0);
// Fetch the list feed of the worksheet.
URL listFeedUrl = worksheet.getListFeedUrl();
ListFeed listFeed = service.getFeed(listFeedUrl, ListFeed.class);
// Create a local representation of the new row.
ListEntry row = new ListEntry();
row.getCustomElements().setValueLocal("firstname", "Joe");
row.getCustomElements().setValueLocal("lastname", "Smith");
row.getCustomElements().setValueLocal("age", "26");
row.getCustomElements().setValueLocal("height", "176");
// Send the new row to the API for insertion.
row = service.insert(listFeedUrl, row);
}
}
seem to be very late but surely this is going to help others! The problem is in your SPREADSHEET_FEED_URL and authentication of SpreadSheetService instance because the official SpreadSheets Api has not shared detailed explaination regarding that.You need to get an authentication token and set it on SpreadSheetService Instance like below to get it work:
private void getAuthenticationToken(Activity activity, String accountName){
//Scopes used to get access to google docs and spreadsheets present in the drive
String SCOPE1 = "https://spreadsheets.google.com/feeds";
String SCOPE2 = "https://docs.google.com/feeds";
String scope = "oauth2:" + SCOPE1 + " " + SCOPE2;
String authenticationToken = null;
try {
accessToken= GoogleAuthUtil.getToken(activity, accountName, scope);
}
catch (UserRecoverableAuthException exception){
//For first time, user has to give this permission explicitly
Intent recoveryIntent = exception.getIntent();
startActivityForResult(recoveryIntent, RECOVERY_REQUEST_CODE);
}catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECOVERY_REQUEST_CODE){
if(resultCode == RESULT_OK){
if(data != null){
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null && !accountName.equals("")){
//To be called only for the first time after the permission is given
getAuthenticationToken(activity, accountName);
}
}else {
Utility.showSnackBar(linearLayout, Constants.INTENT_DATA_NULL);
}
}
}
}
And finally below code to get all spreadsheets in an email account:
public class MySpreadsheetIntegration {
public void getSpreadSheetEntries()
throws AuthenticationException, MalformedURLException, IOException, ServiceException {
SpreadsheetService service =
new SpreadsheetService("MySpreadsheetIntegration-v1");
service = new SpreadsheetService(applicationName);
service .setProtocolVersion(SpreadsheetService.Versions.V3);
service .setAuthSubToken(accessToken);
// Define the URL to request. This should never change.
URL SPREADSHEET_FEED_URL = new URL(
"https://spreadsheets.google.com/feeds/spreadsheets/private/full");
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
List<SpreadsheetEntry> spreadsheets = feed.getEntries();
// Iterate through all of the spreadsheets returned
for (SpreadsheetEntry spreadsheet : spreadsheets) {
// Print the title of this spreadsheet to the screen
System.out.println(spreadsheet.getTitle().getPlainText());
}
}
}
I need to connect and authenticate users from java desk top application , i have tried facebook-java-api using facebookjsonclient and facebookRestclient but not able to get session key. is there any changes in facebook due to which we r not able to connect , or is there asny other best java api or example how to connect. my code is
private static void getUserID(String email, String password){
String session = null;
try {
HttpClient http = new HttpClient();
http.getHostConfiguration().setHost("www.facebook.com");
String api_key = "key";
String secret = "sec";
FacebookJaxbRestClient client = new FacebookJaxbRestClient(api_key, secret);
System.out.println("====>"+client.isDesktop());
String token = client.auth_createToken();
System.out.println(" :::::::"+token);
System.out.println(" :::::::::: "+token);
PostMethod post = new PostMethod("/login.php?");
post.addParameter("api_key", api_key);
post.addParameter("email", email);
post.addParameter("pass", password);
int postStatus = http.executeMethod(post);
System.out.println("url : " + post.getURI());
System.out.println("Response : " + postStatus);
for (Header h : post.getResponseHeaders()) {
System.out.println(h);
}
session = client.auth_getSession(token); // Here I am getting error
System.out.println("Session string: " + session);
long userid = client.users_getLoggedInUser();
//System.out.println("User Id is : " + userid);*/
} catch (FacebookException fe) {
fe.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
AFAIK , there is no way as of now to connect to facebook from a "desktop app" in a straight forward way. You can use apache http client library to mimick a browser and get it done. But it cannot be guranteed to work always.I have also been trying to do it for sometime with some libraries,but they seem broken.
I've had some success doing this. My approach was to use an embedded browser to display the authentication to the user. Facebook handles the authentication and redirects you to a "login successful" page with the access token and expiration time tacked onto the URL as GET data. Most of the code below is for creating and displaying the browser using the org.eclipse.swt library.
private static final String APP_ID = "###########";
private static final String PERMISSIONS =
"COMMA SEPARATED LIST OF REQUESTED PERMISSIONS";
private String access_token;
private long expirationTimeMillis;
/**
* Implements facebook's authentication flow to obtain an access token. This
* method displays an embedded browser and defers to facebook to obtain the
* user's credentials.
* According to facebook, the request as we make it here should return a
* token that is valid for 60 days. That means this method should be called
* once every sixty days.
*/
private void authenticationFlow() {
Display display = new Display();
Shell shell = new Shell(display);
final Browser browser;
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
shell.setLayout(gridLayout);
try {
browser = new Browser(shell, SWT.NONE);
} catch (SWTError e){
System.err.println("Could not instantiate Browser: " + e.getMessage());
display.dispose();
display = null;
return;
}
browser.setJavascriptEnabled(true);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
data.horizontalSpan = 3;
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
browser.setLayoutData(data);
final ProgressBar progressBar = new ProgressBar(shell, SWT.MOZILLA);
data = new GridData();
data.horizontalAlignment = GridData.END;
progressBar.setLayoutData(data);
/* Event Handling */
browser.addProgressListener(new ProgressListener(){
public void changed(ProgressEvent event){
if(event.total == 0) return;
int ratio = event.current * 100 / event.total;
progressBar.setSelection(ratio);
}
public void completed(ProgressEvent event) {
progressBar.setSelection(0);
}
});
browser.addLocationListener(new LocationListener(){
public void changed(LocationEvent e){
// Grab the token if the browser has been redirected to
// the login_success page
String s = e.location;
String token_identifier = "access_token=";
if(s.contains("https://www.facebook.com/connect/login_success.html#access_token=")){
access_token = s.substring(s.lastIndexOf(token_identifier)+token_identifier.length(),s.lastIndexOf('&'));
String expires_in = s.substring(s.lastIndexOf('=')+1);
expirationTimeMillis = System.currentTimeMillis() + (Integer.parseInt(expires_in) * 1000);
}
}
public void changing(LocationEvent e){}
});
if(display != null){
shell.open();
browser.setUrl("https://www.facebook.com/dialog/oauth?"
+ "client_id=" + APP_ID
+ "&redirect_uri=https://www.facebook.com/connect/login_success.html"
+ "&scope=" + PERMISSIONS
+ "&response_type=token");
while(!shell.isDisposed()) {
if(!display.readAndDispatch()){
display.sleep();
if(access_token != null && !access_token.isEmpty()){
try{ Thread.sleep(3000);}catch(Exception e){}
shell.dispose();
}
}
}
display.dispose();
}
}
So all you have to do is figure out what permissions you're going to need to have for your application to work. Be aware that "second dialog" permissions can be picked from by the user so there is no guarantee that you will actually have these permissions.
First off, you need to get the access_token and then I would recommend using restfb library. In order to get the token I would recommend reading this: https://developers.facebook.com/docs/authentication/
A simple summary:
HTTP GET: https://www.facebook.com/dialog/oauth?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream&
response_type=token
Use the code you get from above and then, do: https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&
client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE
Exact the access_token and use FacebookClient from restfb to make the API requests.