Skip to content

Commit

Permalink
Let you exchange a session_key for an access_token
Browse files Browse the repository at this point in the history
  • Loading branch information
Kristján Pétursson authored and mmangino committed Jun 28, 2010
1 parent 7466dab commit 65c9b70
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 76 deletions.
21 changes: 17 additions & 4 deletions lib/mogli/authenticator.rb
@@ -1,22 +1,35 @@
require "cgi"
require 'mogli/client'

module Mogli
class Authenticator
attr_reader :client_id, :secret, :callback_url

def initialize(client_id,secret,callback_url)
@client_id = client_id
@secret = secret
@callback_url = callback_url
end

def authorize_url(options = {})
options_part = "&" + options.map {|k,v| "#{k}=#{v.kind_of?(Array) ? v.join(',') : v}" }.join('&') unless options.empty?
"https://graph.facebook.com/oauth/authorize?client_id=#{client_id}&redirect_uri=#{CGI.escape(callback_url)}#{options_part}"
end

def access_token_url(code)
"https://graph.facebook.com/oauth/access_token?client_id=#{client_id}&redirect_uri=#{CGI.escape(callback_url)}&client_secret=#{secret}&code=#{CGI.escape(code)}"
end


def get_access_token_for_session_key(session_keys)
keystr = session_keys.is_a?(Array) ?
session_keys.join(',') : session_keys
client = Mogli::Client.new
client.post("oauth/exchange_sessions", nil,
{:type => 'client_cred',
:client_id => client_id,
:client_secret => secret,
:sessions => keystr})
end

end
end
57 changes: 32 additions & 25 deletions lib/mogli/client.rb
Expand Up @@ -6,28 +6,28 @@ class Client
attr_reader :access_token
attr_reader :default_params
attr_reader :expiration

include HTTParty
include Mogli::Client::Event
include Mogli::Client::User
include Mogli::Client::User
class UnrecognizeableClassError < Exception; end

def api_path(path)
"https://graph.facebook.com/#{path}"
end

def initialize(access_token = nil,expiration=nil)
@access_token = access_token
# nil expiration means extended access
expiration = Time.now.to_i + 10*365*24*60*60 if expiration.nil? or expiration == 0
@expiration = Time.at(expiration)
@default_params = @access_token ? {:access_token=>access_token} : {}
end

def expired?
expiration and expiration < Time.now
end

def self.create_from_code_and_authenticator(code,authenticator)
post_data = get(authenticator.access_token_url(code))
parts = post_data.split("&")
Expand All @@ -37,42 +37,49 @@ def self.create_from_code_and_authenticator(code,authenticator)
end
new(hash["access_token"],hash["expires"].to_s.to_i)
end


def self.create_from_session_key(session_key, client_id, secret)
authenticator = Mogli::Authenticator.new(client_id, secret, nil)
access_data = authenticator.get_access_token_for_session_key(session_key)
new(access_data['access_token'],
Time.now.to_i + access_data['expires'].to_i)
end

def post(path,klass,body_args)
data = self.class.post(api_path(path),:body=>default_params.merge(body_args))
data = self.class.post(api_path(path),:body=>default_params.merge(body_args))
map_data(data,klass)
end

def delete(path)
self.class.delete(api_path(path),:query=>default_params)
end

def get_and_map(path,klass=nil,body_args = {})
data = self.class.get(api_path(path),:query=>default_params.merge(body_args))
map_data(data,klass)
end

def get_and_map_url(url,klass=nil)
data = self.class.get(url)
map_data(data,klass)
end

def map_data(data,klass=nil)
raise_error_if_necessary(data)
hash_or_array = extract_hash_or_array(data,klass)
hash_or_array = map_to_class(hash_or_array,klass) if klass
hash_or_array
end

#protected

def extract_hash_or_array(hash_or_array,klass)
return nil if hash_or_array == false
return hash_or_array if hash_or_array.nil? or hash_or_array.kind_of?(Array)
return extract_fetching_array(hash_or_array,klass) if hash_or_array.has_key?("data")
return hash_or_array
end

def extract_fetching_array(hash,klass)
f = Mogli::FetchingArray.new
f.concat(hash["data"])
Expand All @@ -84,7 +91,7 @@ def extract_fetching_array(hash,klass)
end
f
end

def map_to_class(hash_or_array,klass)
return nil if hash_or_array.nil?
if hash_or_array.kind_of?(Array)
Expand All @@ -93,24 +100,24 @@ def map_to_class(hash_or_array,klass)
hash_or_array = create_instance(klass,hash_or_array)
end
end

def create_instance(klass,data)
klass = determine_class(klass,data)
if klass.nil?
raise UnrecognizeableClassError.new("unable to recognize klass for #{klass.inspect} => #{data.inspect}")
end
klass.new(data,self)
end

def constantize_string(klass)
klass.is_a?(String) ? Mogli.const_get(klass) : klass
end

def determine_class(klass_or_klasses,data)
klasses = Array(klass_or_klasses).map { |k| constantize_string(k)}
klasses.detect {|klass| klass.recognize?(data)} || klasses.first
end

def raise_error_if_necessary(data)
if data.kind_of?(Hash)
if data.keys.size == 1 and data["error"]
Expand All @@ -120,22 +127,22 @@ def raise_error_if_necessary(data)
end
end
end

def fields_to_serialize
[:access_token,:default_params,:expiration]
end

# Only serialize the bare minimum to recreate the session.
def marshal_load(variables)#:nodoc:
fields_to_serialize.each_with_index{|field, index| instance_variable_set("@#{field}", variables[index])}
end

# Only serialize the bare minimum to recreate the session.
# Only serialize the bare minimum to recreate the session.
def marshal_dump#:nodoc:
fields_to_serialize.map{|field| send(field)}
end

# Only serialize the bare minimum to recreate the session.
# Only serialize the bare minimum to recreate the session.
def to_yaml( opts = {} )#nodoc
YAML::quick_emit(self.object_id, opts) do |out|
out.map(taguri) do |map|
Expand All @@ -145,6 +152,6 @@ def to_yaml( opts = {} )#nodoc
end
end
end

end
end
46 changes: 37 additions & 9 deletions spec/authenticator_spec.rb
@@ -1,32 +1,60 @@
require "spec_helper"
describe Mogli::Authenticator do

let :authenticator do
Mogli::Authenticator.new("123456","secret","http://example.com/url")
end

it "has the client id" do
authenticator.client_id.should == "123456"
end

it "has the secret" do
authenticator.secret.should == "secret"
end

it "has the callback url" do
authenticator.callback_url.should == "http://example.com/url"
end

it "creates the authorize_url" do
authenticator.authorize_url.should == "https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl"
end

it "creates the authorize_url with scopes as an array" do
authenticator.authorize_url(:scope=>[:user_photos,:user_videos,:stream_publish]).should ==
"https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&scope=user_photos,user_videos,stream_publish"
authenticator.authorize_url(:scope=>[:user_photos,:user_videos,:stream_publish]).should ==
"https://graph.facebook.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&scope=user_photos,user_videos,stream_publish"
end

it "creates the access_token_url" do
authenticator.access_token_url("mycode").should == "https://graph.facebook.com/oauth/access_token?client_id=123456&redirect_uri=http%3A%2F%2Fexample.com%2Furl&client_secret=secret&code=mycode"
end

it "can trade session_keys for access_tokens" do
Mogli::Client.should_receive(:post).
with("https://graph.facebook.com/oauth/exchange_sessions",
:body => {:type => "client_cred", :client_id => "123456",
:client_secret => "secret",
:sessions => "mysessionkey,yoursessionkey"}).
and_return([{"access_token" => "myaccesstoken", "expires" => 5000},
{"access_token" => "youraccesstoken", "expires" => 5000}])

authenticator.
get_access_token_for_session_key(["mysessionkey","yoursessionkey"]).
should == [{"access_token" => "myaccesstoken", "expires" => 5000},
{"access_token" => "youraccesstoken", "expires" => 5000}]
end

it "can trade one session_key for an access_tokens" do
Mogli::Client.should_receive(:post).
with("https://graph.facebook.com/oauth/exchange_sessions",
:body => {:type => "client_cred", :client_id => "123456",
:client_secret => "secret", :sessions => "mysessionkey"}).
and_return({"access_token" => "myaccesstoken", "expires" => 5000})

authenticator.
get_access_token_for_session_key("mysessionkey").
should == {"access_token" => "myaccesstoken", "expires" => 5000}
end

end

0 comments on commit 65c9b70

Please sign in to comment.