app/controllers/facebook_controller.rb
# post /facebook = facebook/create is called when facebook starts the APP.
# / will route to this method if :fb_locale and :signed_request are in params (see routes.rb root and /lib/role_constraints.rb)
# Signature 1: when an unauthorized user starts the app from facebook
#   input: signed_request encoded JSON hash with (user, issued_at and algorithm)
#          1) user 	      A JSON array containing the locale string, country string and the age object (containing the min and max numbers of the age range) for the current person using the app.
#          2) algorithm 	A JSON string containing the mechanism used to sign the request.
#          3) issued_at 	A JSON number containing the Unix timestamp when the request was signed.
#   example: hash = { "algorithm"=>"HMAC-SHA256",
#                     "issued_at"=>1373284394,
#                     "user"=>{"country"=>"dk", "locale"=>"da_DK", "age"=>{"min"=>21}}}
#   action: redirect to https://www.facebook.com/dialog/oauth?client_id=<client_id>&redirect_uri=<current_url> for FB app logn / authorization
#           must be a redirect to top frame (top.location.href)
# Signature 2: when an authorized user starts the app from facebook
#   Parameters: {"signed_request"=>"8AsUxKh02db56T9oVjnCe3EfaNUXw8qqHNHmcQaDhgA.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzOTY0MjU2MDAsImlzc3VlZF9hdCI6MTM5NjQyMTM1Miwib2F1dGhfdG9rZW4iOiJDQUFGalpCR3p6T2tjQkFQSkNMUXZma3BoNjBmbUl1WkJJUUVpeFpDa1ByQXQzbURJRnpiUHUxeGpxMlZlNkZqUldvM3huUWdXWW1EM3V3ekJzWkJzRGFRNzNXeFZQbDNaQkVzTVpCREFGQm9lR0VKWkJ2SHdmS2hiblZQWkJVVExNRWdXREJSUXBjTUp2Ym1UWkMyR0VNelZzMDcyVlczWkFkS3BYY0owZENuWkNsM1d1QVVXdFlLVjh0c00xOVFzbWowZzZFWkQiLCJ1c2VyIjp7ImNvdW50cnkiOiJkayIsImxvY2FsZSI6ImVuX0dCIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjE3MDU0ODEwNzUifQ",
#                "fb_locale"=>"en_GB"}
#   input: signed_request encoded JSON hash with (user, issued_at, algorithm, expires, oauth_token and user_id)
#   example: hash = { "algorithm"=>"HMAC-SHA256",
#                     "expires"=>1373374800,
#                     "issued_at"=>1373370798,
#                     "oauth_token"=>"CAAFjZBGzzOkcBAM3vNXbvDtDm3qcV7RQ3HwSRZAC9PRUiSvA8zLAnFFUwEmuV5t6fWohIPn8ZCDUrTZCUFtlUdOK1aSPhL1nvobmEBNucZBu9KLWqb4wRcB6jZBy6K49pZCQOaXRl0C8cj4ZCAYfdZC9tZCLC8wZAFhs9GWJMGIkO91AwZDZD",
#                     "user"=>{"country"=>"dk", "locale"=>"da_DK", "age"=>{"min"=>21}},
#                     "user_id"=>"1705481075"}
#   note that signed_request signature is identical in signature 2 and signature 4. Only params signature is different.
#   action:
# Signature 3: when an authorized user deauthorise gofreerev in facebook (advanced settings - deauthorise dallback url)
#   input: signed_request encoded JSON hash with (user, issued_at, algorithm, and user_id)
#   example: hash = {"algorithm"=>"HMAC-SHA256",
#                    "issued_at"=>1391445050,
#                    "user"=>{"country"=>"dk", "locale"=>"en_GB"},
#                    "user_id"=>"1705481075"}
#   action:
# Signature 4: when app starts from a friends_find facebook notification. See User.find_friends_batch
#   Parameters: {"signed_request"=>"6xbhSI-JNpGOf7Ye54gft7kF4Tmxdr0AQVA0Iy0hw34.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzOTY0MjIwMDAsImlzc3VlZF9hdCI6MTM5NjQxNzY4Mywib2F1dGhfdG9rZW4iOiJDQUFGalpCR3p6T2tjQkFQUGoyakJtVTc2UkxkODFjcG0xSTVqMDlZcWNZNjFSOFRwYnRoa3l0QlNkb0JoalNrQWh0UFBhaTN3VmlCekZRM2dsb1pCVUtxTVhXV0tlTkVqeVk3U2pOTXJRZXUzWkI4MVRoVEhwTEtBZzMyRzlLaVpCaFpCR1pBbEdROFpBQThnN3R5aDZVREpRS0pqY3dnek52djZqaE9lR00yUlUyTEk1MkZDWkFIZUJaQWdSWVVWZXVTRHNzamdrYjVKcTNBWkRaRCIsInVzZXIiOnsiY291bnRyeSI6ImRrIiwibG9jYWxlIjoiZW5fR0IiLCJhZ2UiOnsibWluIjoyMX19LCJ1c2VyX2lkIjoiMTcwNTQ4MTA3NSJ9",
#                "fb_locale"=>"en_GB", "fb_source"=>"notification", "fb_ref"=>"friends_find",
#                "ref"=>"notif", "notif_t"=>"app_notification"}
#   signed_request encoded JSON hash with (user, issued_at, algorithm, expires, oauth_token and user_id)
#   example: hash = {"algorithm"=>"HMAC-SHA256",
#                    "expires"=>1396422000,
#                    "issued_at"=>1396417683,
#                    "oauth_token"=>"CAAFjZBGzzOkcBAPPj2jBmU76RLd81cpm1I5j09YqcY61R8TpbthkytBSdoBhjSkAhtPPai3wViBzFQ3gloZBUKqMXWWKeNEjyY7SjNMrQeu3ZB81ThTHpLKAg32G9KiZBhZBGZAlGQ8ZAA8g7tyh6UDJQKJjcwgzNvv6jhOeGM2RU2LI52FCZAHeBZAgRYUVeuSDssjgkb5Jq3AZDZD",
#                    "user"=>{"country"=>"dk", "locale"=>"en_GB", "age"=>{"min"=>21}},
#                    "user_id"=>"1705481075"}
#   note that signed_request signature is identical in signature 2 and signature 4. Only params signature is different.
#   action:
post /facebook = facebook/create is called when facebook starts the APP.
will route to this method if :fb_locale and :signed_request are in params
Signature 1: when an unauthorized user starts the app from facebook
input: signed_request encoded JSON hash with (user, issued_at and algorithm)
1) user             A JSON array containing the locale string, country string and the age object (containing the min and max numbers of the age range) for the current person using the app.
2) algorithm  A JSON string containing the mechanism used to sign the request.
3) issued_at  A JSON number containing the Unix timestamp when the request was signed.
post / facebook = facebook / create is called when facebook starts the APP . will route to this method if : fb_locale and : signed_request are in params Signature 1 : when an unauthorized user starts the app from facebook input : signed_request encoded JSON hash with ( user issued_at and algorithm ) 1 ) user A JSON array containing the locale string country string and the age object ( containing the min and max numbers of the age range ) for the current person using the app . 2 ) algorithm A JSON string containing the mechanism used to sign the request . 3 ) issued_at A JSON number containing the Unix timestamp when the request was signed .
def create
    logout(provider)
    signed_request = params[:signed_request]
    logger.debug2  "signed_request = #{signed_request}"
    if signed_request.to_s == ''
      logger.debug2  'fatal error - signed_request parameter was missing in request'
      render_with_language('cross_site_forgery')
      return
    end
    oauth = Koala::Facebook::OAuth.new(API_ID[provider], API_SECRET[provider], API_CALLBACK_URL[provider])
    hash = oauth.parse_signed_request(signed_request)
    logger.debug2  "hash = #{hash}"
    locale = hash['user']['locale']
    language = locale.to_s.first(2)
    set_session_value(:language, valid_locale(language)) unless valid_locale(get_session_value(:language))
    logger.debug2  "session[:language] = #{get_session_value(:language)}"
    if hash.has_key?('oauth_token') && hash.has_key?('user_id')
      if params[:fb_ref].to_s == 'friends_find'
        signature = 4 
      else
        signature = 2 
      end
      logger.debug2  'user has already authorized gofreerev'
      logger.debug2  "user_id #{hash['user_id']}"
      viewname = 'autologin'
      @disable_ajax_tasks = true
    elsif hash.has_key?('user_id')
      signature = 3
      viewname = 'deauthorize'
    else
      signature = 1
      viewname = __method__
    end
    if signature == 3
      user_id = "#{hash['user_id']}/facebook"
      user = User.find_by_user_id(user_id)
      user.update_attribute(:deauthorized_at, Time.new) if user
    else
      if signature == 4
        context = 'friends_find'
      else
        context = 'login'
      end
      @auth_url =  oauth.url_for_oauth_code :state => set_state_tasks_store(context)
      @auth_url = @auth_url.gsub('&amp;', '&') 
      logger.debug2  "@auth_url = #{@auth_url}"
    end
    render_with_language viewname
  end
def create logout ( provider ) signed_request = params [ :signed_request ] logger . debug2 "signed_request = #{signed_request}" if signed_request . to_s == '' logger . debug2 'fatal error - signed_request parameter was missing in request' render_with_language ( 'cross_site_forgery' ) return end oauth = Koala :: Facebook :: OAuth . new ( API_ID [ provider ] , API_SECRET [ provider ] , API_CALLBACK_URL [ provider ] ) hash = oauth . parse_signed_request ( signed_request ) logger . debug2 "hash = #{hash}" locale = hash [ 'user' ] [ 'locale' ] language = locale . to_s . first ( 2 ) set_session_value ( :language , valid_locale ( language ) ) unless valid_locale ( get_session_value ( :language ) ) logger . debug2 "session[:language] = #{get_session_value(:language)}" if hash . has_key? ( 'oauth_token' ) && hash . has_key? ( 'user_id' ) if params [ :fb_ref ] . to_s == 'friends_find' signature = 4 else signature = 2 end logger . debug2 'user has already authorized gofreerev' logger . debug2 "user_id #{hash['user_id']}" viewname = 'autologin' @disable_ajax_tasks = true elsif hash . has_key? ( 'user_id' ) signature = 3 viewname = 'deauthorize' else signature = 1 viewname = __method__ end if signature == 3 user_id = "#{hash['user_id']}/facebook" user = User . find_by_user_id ( user_id ) user . update_attribute ( :deauthorized_at , Time . new ) if user else if signature == 4 context = 'friends_find' else context = 'login' end @auth_url = oauth . url_for_oauth_code :state => set_state_tasks_store ( context ) @auth_url = @auth_url . gsub ( '&amp;' , '&' ) logger . debug2 "@auth_url = #{@auth_url}" end render_with_language viewname end
post /facebook = facebook/create is called when facebook starts the APP.
post / facebook = facebook / create is called when facebook starts the APP .
# logout any old facebook user before (new) login # signed_request = 9m_Xew0oojeuzMjIZFqwx9lI_UI4AqC-vMWXL9o45g4.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZF9hdCI6MTM3MzI4NDA4OCwidXNlciI6eyJjb3VudHJ5IjoiZGsiLCJsb2NhbGUiOiJkYV9ESyIsImFnZSI6eyJtaW4iOjIxfX19 # fatal error - signed_request parameter was missing in request # unpack unsigned request # hash = {"algorithm"=>"HMAC-SHA256", "issued_at"=>1373284394, "user"=>{"country"=>"dk", "locale"=>"da_DK", "age"=>{"min"=>21}}} # fix invalid or missing language for translate # todo: check if user already has authorized the required FB privileges # hash: hash = {"algorithm"=>"HMAC-SHA256", "expires"=>1373374800, "issued_at"=>1373370798, #               "oauth_token"=>"CAAFjZBGzzOkcBAM3vNXbvDtDm3qcV7RQ3HwSRZAC9PRUiSvA8zLAnFFUwEmuV5t6fWohIPn8ZCDUrTZCUFtlUdOK1aSPhL1nvobmEBNucZBu9KLWqb4wRcB6jZBy6K49pZCQOaXRl0C8cj4ZCAYfdZC9tZCLC8wZAFhs9GWJMGIkO91AwZDZD", #               "user"=>{"country"=>"dk", "locale"=>"da_DK", "age"=>{"min"=>21}}, "user_id"=>"1705481075"} # signature 2 - authorization in progress - show autologin page and redirect to facebook to complete authorization # signature 4 - as signature 2 - but redirect to users/index?friends=find after login # login and redirect to users/index?friends=find page # login and redirect to gifts/index page # logger.debug2  "oauth_token = #{hash['oauth_token']}" # oauth_token = CAAFjZBGzzOkcBAB0GGUE4i4O9ZAT6FJ1N3U3mhl7S1TELLRl514fdEZAMuyMY4iUmFt74bBUWRH9WM4LYafsxs6osDzJc0ARo1yhRZCbAeQo0A9RXh5yEEW9cC3SjKC4LqbeO8x2Qy6JINJGO1pSkTllmQp5jPMZD # user_id  = 1705481075 # autologin redirect to https://www.facebook.com/dialog/oauth? to get code without showing create.html erb page # wait with post login tasks to gifts/index page # signature 3 - used has deauthorization gofreerev in facebook # signature 1 - new unauthorized user # create.html.erb shows an intro page with an authorize link # FB deauthorize - user has removed Gofreerev from app settings page in facebook # a deleted marked user account (users/edit delete link) will be deleted within 6 minutes (CLEANUP_USER_DELETED) # a deauthorized user acccount will be deleted after 14 days (CLEANUP_USER_DEAUTHORIZED) # a inactive user account will be deleted after 1 year (CLEANUP_USER_INACTIVE) # todo: double check that user has deauthorized gofreerev. http://stackoverflow.com/questions/5623035/facebook-app-users-list. Maybe user.is_app_user can be used # FB authorization with minimal permissions (information already public) # note that there are problems with cookie store and IE10 when login starts from facebook (session[:state] not preserved) # tasks table is used for temporary store of state in facebook/index => autologin => FB => facebook/index sequence # fix invalid escape in auth url # show_friend page with an introduction and a authorize link - use create-<language>.html.erb if the view exists