Graph API Edge Case: Publishing AS a Page TO a Group

July 14, 2018

What we wanted

Quite simply: to be able to post to a facebook group AS a page

Post to a group as the admin of we&co's fan page (as We&co) to a Facebook group that our company created to tell people about jobs in atlanta automatically when on of our clients adds a new job posting. Use Case: Company A posts a job for a software engineer in Atlanta. We have a group called Atlanta Jobs (or something like that) in which we'd like to post the job saying that company A is hiring for software engineering. We'd like to this post to be from We&Co in other words, posted under the name of the fan page we manage.

Small Issue: Posting to a group as a page is not strictly possible.

From all of my research and efforts it doesn't seem that it is possible to post using the graph api to an external group AS the admin (and under the name of) a fan page. Of course if you are aware of a method of accomplishing this, please let me know

Getting Started: Facebook Administration

Since we werent able to post using a page, we decided to Created a Facebook user that we'd use to post to the group: Step 1: Create a user (we named it We&Co Jobs) Step 2: Make user an admin of the group you'd like to post to (you'll have to friend request them unfortunately) Step 3: Make the user an admin of the app you plan on using to post on the users behalf (explained below)

Setting up your facebook apps and accounts

In order to make calls with the api, you'll need to first setup an app through Facebook Developers that we will use to post on behalf of the user we created Step 1: Create your production app using the instructions on facebooks site Step 2: Create a test app: we will use for all testing purposes.

  • This is important because we will be asking for the "extended permission": user_ managed _groups
  • user_managed_groups will allow us to eventually post to our facebook group via the omniauth, and koala gems, explained below
  • using extended permissions in a production app require going through a special review process in order to turn on the features. If you plan on using this app in production you'll want to look into that review process, but for now lets just use the keys from your test app to get things working
  • Facebook doesn't let you point your app to localhost so you put in a fake domain as a placeholder but it's not arbitrary, as you'll later point your localhost entry in your hosts file to that domain to make the graph api happy when you are making calls from localhost
  •  photo Screen Shot 2015-06-04 at 1.51.26 PM_zps40s1zhra.png
     photo Screen Shot 2015-06-04 at 2.04.44 PM_zpsy7wnfcd8.png

    Editing your hosts file to point your localhost to your test address

    We gave our facebook app a dummy address that we said we'd be making api calls from, now we need to add that address to our host file and point it to localhost in order to make us look like we are coming from that domain. Step 1: Locate your host file Step 2: Add the following line, pointing localhost to the domain you gave your test app: # Pointing localhost to testapp 127.0.0.1 dev.testapp.com Step 3: From now on when you are testing your app, navigate to dev.testapp.com instead of localhost:3000, this will insure facebook thinks we are coming from the correct address

  • NOTE: 127.0.0.1 is the localhost address for macs, it may be different for other systems
  • Rails app setup: Koala and Omniauth

    We will use Omniauth to connect to facebook using a login flow, and once we have a unique access_token for our user, Koala will be used to post to our group page Step 1: Check out the Documentation for the omniauth, and koala gems Step 2: Add the following lines to your gem file gem 'omniauth-facebook' gem 'koala' Step 3: bundle install, also make sure you have your facebook app id and facebook secret stored somewhere save you can access later Step 4: Create an omniauth.rb file, this file will allow us to use our omniauth gem to establish a connection with facebook in which we will recieve a valid access_token (notice we are asking for the user_managed_groups permission here) Rails.application.config.middleware.use OmniAuth::Builder do provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], scope: ['email', 'user_managed_groups', 'publish_actions', 'taggable_friends'] end Step 5: finish setting up the login process, if you need help with this there is a railscast Step 6: Make the users credentials persist in the database. The way we accomplished this was adding a provider callback in routes.rb: match "/auth/:provider/callback", to: "main#facebook_auth", via: [:get, :post] and the facebook_auth method looks like this: def facebook_auth fb_auth = request.env['omniauth.auth'] #fb_auth is an object containing lots of information about the authentication established #you can persist the user in the database by storing information from this object e.g.: if User.find_by(facebook_id: fb_auth.uid).nil? # create a new user else # user = User.find_by(facebook_id: fb_auth.uid) # etc end Notes: here, facebook_id is a field that we've persisted in the database that represents the users facebook_id so we can use it later. There are also fields called facebook_token and fbtoken_expires_on that we will use later to make sure that token is always good when we are posting with our 1 authorized internal user.

    Using koala, renewing tokens, posting to the book

    Setup: We submit a form or something, and would like to post things from our model to facebook arranged in a certain way Step 1: Make sure the token we have is still valid when we get ready to try and post something to facebook: class PublishFacebookWorker include Sidekiq::Worker def perform(user) # facebook group id of the group you'd like to post in facebook_access_token = get_or_renew_access_token(user) weco_link = "www.somelinkidliketopost.com" graph = Koala::Facebook::API.new(facebook_access_token) graph.put_object( facebook_group_id, "feed", message: "Attention people here is a message with users name: #{user.name} and a link parameter", link: "weco_link" ) end end Notes: I chose to put this in a worker using Sidekiq this is usually good practice with involved api calls so we can keep the app moving along def get_or_renew_access_token(user) token_expiry = user.fbtoken_expires_on if token_expiry.to_i <= Time.now.to_i new_token = facebook_oauth.exchange_access_token_info(user.facebook_token) user.facebook_token = new_token['access_token'] # facebook gives us the new_token expiration in int form so we need to do this funky conversion and addition user.fbtoken_expires_on = Time.at(Time.now.to_i + new_token['expires'].to_i) identity.save! end return user.facebook_token end Notes: The value of new_token['expires'] is an int representing the number of seconds until your current token expires (60 days). I simply converted the current time to the same format and added them together to set my persistent fbtoken_expires_on field associated with my user def facebook_oauth @facebook_oauth ||= Koala::Facebook::OAuth.new(ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET']) end end

    Additional footnotes and helpful links

  • Mentioning other users or entities in a post using the graph api: I did not find this to be possible
  • A good testing tool for api calls: The Graph API Explorer
  • Facebook Documentation on mentioning in posts: I was not able to get this to work and later read that this functionality had been depreciated
  • Access Tokens Documentation
  • Posting as a page to the page of which you have administrative permissions using koala
  • Difference between a page and a group
  • Testing a local facebook connection
  • Editing the hosts file on a mac
  • Renewing an access_token using koala
  • A comprehensive list of permissions that you could ask of your users
  • bit.ly gem for rails: for shortening URLs
  • A code snippet outlining error handling for koala oauth: for shortening URLs