Facebook Graph API 2022 — Auto-Post on Behalf of the User

Luciana
6 min readJun 22, 2022

If you missed my previous post, read it here. In this post, I’ll go over step-by-step how to use the Facebook Graph API to auto-post on behalf of the users’ Facebook feed.

Step 1: Create an App on Facebook Developers: Go to developers.facebook and follow this Facebook guide. When you get to the App Dashboard select Facebook Login.
Inside your App Dashboard, go to App Reviews > Permission and Features and click “Request advanced access” on these 4 features:

1. pages_read_engagement
2. pages_show_list
3. pages_manage_posts
4. public_profile

These are the permissions that you’ll need in order to post on behalf of the user. You’ll instantly get the public_profile but for the other three, you’ll need to build your app first and then request to Facebook a review for them to grant your app access to these.
Your “test” App (before the Facebook review) will only work on accounts that are part of the App Role (you can add or remove people inside the App Dashboard > Roles > Roles), there’s also a Test User Account that you can use it (App Dashboard > Roles > Test Users).

You’ll also need to install a ngrok or similar to run the Facebook features because it doesn’t work on a localhost port.
Every time that you create a new ngrok port, don’t forget to add the URL inside the App Dashboard > Facebook Login > Settings > Client OAuth Settings > Allowed Domains for the JavaScript SDK. This is where you’ll add all the URLs that you want to your App recognized once it’s fully working.

Step 2: Basic Facebook Settings: At the beginning of your code file you’ll need to include these Facebook settings.
References: Quickstart: Facebook SDK for JavaScript and Code Configurator

window[‘fbAsyncInit’] = function() {
FB.init({
appId: ‘your-app-id’,
cookie: true,
xfbml: true,
version: ‘v14.0’
});
FB.AppEvents.logPageView();
};
(function(d,s,id) {
var js,fjs=d.getElementsByTagName(s)[0];
if(d.getElementById(id)) { return; }
js=d.createElement(s); js.id=id;
js.src=”https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js,fjs);
}(document,’script’,’facebook-jssdk’));

Step 3: Getting The User Login Credentials: Some things to consider before straight adding the login function. Is it a new or a returning user? If it’s a returning user, did this user connect his Facebook and your App all the way or stopped in the middle (didn’t select a Facebook page, for example)?

Initially, Facebook gives a shorter-lived token, that becomes invalid after a few hours. You can use this shorter-lived token to get a long-lived token, that doesn’t expire or if it does, it’s after 3 months without being used.

Case New User: A Facebook Login pop-up will open. The user will add their credentials (login/password), select Facebook page(s), and grant all the permissions (inside the scope).
If everything is smoothly, a getUserLongLivedToken(userId, userShortLivedToken) is called, if the user uncheck/toggle one or more permission, a getDeclinedPermissions(declinedPermissions) is called.

function getUserLogin() {
return new Promise(function(resolve) {
FB.login(resolve, { scope: ‘public_profile, pages_manage_posts, pages_read_engagement, pages_show_list’, return_scopes: true });
}).then(response => {

const permissions = [‘public_profile’, ’pages_manage_posts’, ’pages_read_engagement’, ’pages_show_list’]
let grantedPermissions = permissions.every(per => response.authResponse.grantedScopes.includes(per)) if (response.status === ‘connected’ && !!grantedPermissions) {
let userId = response.authResponse.userID
let userShortLivedToken = response.authResponse.accessToken
return getUserLongLivedToken(userId, userShortLivedToken)
} else if (response.status === ‘connected’ && !grantedPermissions) {
let declinedPermissions = permissions.filter(per => !response.authResponse.grantedScopes.includes(per))
return getDeclinedPermissions(declinedPermissions)
}
});
};

Step 4: Get the Longer Lived Token from the User. Here is where you’ll fetch and save the user info in your database. Note that I have a variable called FB_AUTH that was initially declared so I can get the user saved on the database.

function getUserLongLivedToken(userId, userShortLivedToken) {
return fetch('/social/facebook', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
facebook_user_id: userId,
facebook_user_short_lived_token: userShortLivedToken,
})
}).then(res => {
return res.json()
.then(data => {
FB_AUTH = data;
return data.facebook_user_token;
});
})
};

Re-ask the denied permissions. When this case happens, what we need to do is re-open the Facebook Login pop-up once again and make sure the user accepts all the permissions. Before it happens, I added a modal explaining to the user that the app needs all the permissions granted in order to work, and then I called the reAskForDeclinedPermissions().
If the user once again, declined one or more permissions, it’ll be a loop (modal and reAskForDeclinedPermissions). If the user grants all permissions, it’ll call the getUserLongLivedToken function.

function reAskForDeclinedPermissions(declinedPermissions) {
return new Promise(function(resolve) {
FB.login(resolve, { scope: declinedPermissions.toString(), auth_type: 'rerequest', return_scopes: true });
}).then(response => {
let reRequestPermissions = declinedPermissions.every(per => response.authResponse.grantedScopes.includes(per)) if (response.status === 'connected' && !reRequestPermissions) {
declinedPermissions = declinedPermissions.filter(per => !response.authResponse.grantedScopes.includes(per))
return getDeclinedPermissions(declinedPermissions)
} else {
let userId = response.authResponse.userID
let userShortLivedToken = response.authResponse.accessToken
return getUserLongLivedToken(userId, userShortLivedToken)
}
});
};

Step 5: Get the User’s Facebook Page(s). The user MUST be an admin of the page in order for the App post on their behalf. The whole idea is that the App helps the admin user administrate the page. There’re many other options inside the fields, you can check here and see what information you want to get.
Here, I’m fetching and saving the user’s Facebook Page(s) in the database.

function getUserPages() {
return new Promise(function(resolve) {
FB.api(`/${FB_AUTH.facebook_user_id}/accounts?fields=access_token,category,name,link,tasks&access_token=${FB_AUTH.facebook_user_token}`, resolve)
}).then(response => {
if (response.error) {
throw Object.assign(new Error(), response.error);
} else {
let adminPages = response.data.filter(page => page.tasks.includes('MANAGE'))
return fetch(`/social/facebook/${FB_AUTH.facebook_user_token}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pages: adminPages })
}).then(res => res.json())
.then(data => {
FB_AUTH.authenticated_pages = data.authenticated_pages
return data.authenticated_page
})
}
});
};

Step 6: Submit The Post. If the user has more than one page, a custom modal with all the pages will show up to the user selects which one it’ll be used (in case, the user has just one page only, this modal doesn’t show up).
After the user selects the Facebook page, another custom modal opens up with the preview image and caption. The user has the ability to update the caption. When the user clicks to post, a submitPost() function is called, which contains a fetch to save the information in the database.

function submitPost(pageId, image, caption, item_id, pageToken, pageLink) {
return new Promise(function(resolve) {
FB.api(`/${pageId}/photos`, 'POST', { message: caption, url: image, access_token: pageToken, fields: 'created_time,from,id' }, resolve)
}).then(response => {
if (response.error) {
throw Object.assign(new Error(), response.error);
} else {
return fetch(`/social/facebook/${pageId}/post`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
facebook_user_id: FB_AUTH.facebook_user_id,
facebook_page_id: response.from.id,
facebook_page_name: response.from.name,
facebook_post_id: response.id,
facebook_post_created_at: response.created_time,
facebook_post_image_url: image,
facebook_post_caption: caption,
item_id: item_id
})
}).then(res => res.json())
.then(data => {
FB_AUTH.posts.push(data)
return postConfirmation(response.from.name, pageLink)
})
}
})
};

A custom modal with a confirmation shows up with a link to the Facebook Page that was posted.

Extra: Additional functions that I used

Check Access Token. This function checks if the user’s token is valid.
If it’s not, it’ll call the getUserLogin().

async function checkAccessToken() {
if (!FB_AUTH) await getUserLogin()
return new Promise(function(resolve) {
FB.api(`/me?access_token=${FB_AUTH.facebook_user_token}`, resolve);
}).then(response => {
if (response.error) {
return new Promise(function(resolve) {
FB.getLoginStatus(resolve)
}).then(response => {
if (response.status === 'connected') {
let userId = response.authResponse.userID
let userShortLivedToken = response.authResponse.accessToken
return checkPermissions(userId, userShortLivedToken)
} else {
return getUserLogin()
};
});
}
return response;
})
};

Check Permissions. If the user token is valid, it needs to check if the permissions were all granted, calling checkPermissions().

function checkPermissions(userId, userShortLivedToken) {
return new Promise(function(resolve) {
FB.api('/me/permissions', resolve);
}).then(response => {
if (response.error) {
return getUserLogin();
} else {
const declinedPermissions = response.data.some(d => d.status != 'granted');
if (declinedPermissions) {
return getDeclinedPermissions(declinedPermissions);
} else {
return getUserLongLivedToken(userId, userShortLivedToken);
}
}
});
};

If the user wants to Add a Page that is not listed, it’ll call the reauthorize function that will disconnect the user from the App and open the Facebook login pop-up.

function reauthorize(image, caption, item_id) {
loadingAnimation();
FB.getLoginStatus(function(response) {
if (response && response.status === 'connected') {
FB.logout();
}
});
return new Promise(function(resolve) {
FB.api(`/${FB_AUTH.facebook_user_id}/permissions?access_token=${FB_AUTH.facebook_user_token}`, 'DELETE', resolve)
}).then(response => {
if (response.success === true) {
fetch(`/social/facebook/${FB_AUTH.facebook_user_token}`, { method: 'DELETE' })
}
return openShareModal(image, caption, item_id)
});
};

Facebook API Limitations

  • Only for Facebook Business Pages (does not work on Personal Accounts or Groups)
  • The user must be an admin of the page (other roles than that, the page won’t be available to be posted)
  • To check: Go to the Facebook Page Settings > Page Roles

--

--