diff --git a/.gitignore b/.gitignore index c795954..7e0d7ad 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ rdoc pkg ## PROJECT::SPECIFIC +*.gem \ No newline at end of file diff --git a/Rakefile b/Rakefile index 1e58d72..e86c8e8 100644 --- a/Rakefile +++ b/Rakefile @@ -4,7 +4,7 @@ require 'term/ansicolor' include Term::ANSIColor -OMNIAUTH_GEMS = %w(oa-core oa-basic oa-oauth oa-openid) +OMNIAUTH_GEMS = %w(oa-core oa-basic oa-oauth oa-openid oa-facebook) desc 'Run specs for all of the gems.' task :spec do @@ -15,4 +15,6 @@ task :spec do end end end + +task :default => :spec \ No newline at end of file diff --git a/oa-basic/CHANGELOG.rdoc b/oa-basic/CHANGELOG.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-basic/LICENSE.rdoc b/oa-basic/LICENSE.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-basic/README.rdoc b/oa-basic/README.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-basic/oa-basic.gemspec b/oa-basic/oa-basic.gemspec index 1a2e634..dd00a92 100644 --- a/oa-basic/oa-basic.gemspec +++ b/oa-basic/oa-basic.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |gem| gem.summary = %Q{HTTP Basic strategies for OmniAuth.} gem.description = %Q{HTTP Basic strategies for OmniAuth.} gem.email = "michael@intridea.com" - gem.homepage = "http://github.com/intridea/omni_auth" + gem.homepage = "http://github.com/intridea/omniauth" gem.authors = ["Michael Bleigh"] gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) diff --git a/oa-core/CHANGELOG.rdoc b/oa-core/CHANGELOG.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-core/LICENSE.rdoc b/oa-core/LICENSE.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-core/README.rdoc b/oa-core/README.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-core/lib/omniauth/strategy.rb b/oa-core/lib/omniauth/strategy.rb index d10a40c..8c1b031 100644 --- a/oa-core/lib/omniauth/strategy.rb +++ b/oa-core/lib/omniauth/strategy.rb @@ -12,17 +12,21 @@ module OmniAuth end def call(env) - dup._call(env) + dup.call!(env) end - def _call(env) + def call!(env) @env = env if request.path == "#{OmniAuth.config.path_prefix}/#{name}" request_phase elsif request.path == "#{OmniAuth.config.path_prefix}/#{name}/callback" callback_phase else - @app.call(env) + if respond_to?(:other_phase) + other_phase + else + @app.call(env) + end end end @@ -31,7 +35,8 @@ module OmniAuth end def callback_phase - raise NotImplementedError + request['auth'] = auth_hash + @app.call(env) end def auth_hash diff --git a/oa-core/oa-core.gemspec b/oa-core/oa-core.gemspec index fdfe91a..9f34b3f 100644 --- a/oa-core/oa-core.gemspec +++ b/oa-core/oa-core.gemspec @@ -1,10 +1,10 @@ Gem::Specification.new do |gem| - gem.name = "oa-basic" + gem.name = "oa-core" gem.version = File.open(File.dirname(__FILE__) + '/VERSION', 'r').read.strip gem.summary = %Q{HTTP Basic strategies for OmniAuth.} gem.description = %Q{HTTP Basic strategies for OmniAuth.} gem.email = "michael@intridea.com" - gem.homepage = "http://github.com/intridea/omni_auth" + gem.homepage = "http://github.com/intridea/omniauth" gem.authors = ["Michael Bleigh"] gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) diff --git a/oa-facebook/CHANGELOG.rdoc b/oa-facebook/CHANGELOG.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-facebook/LICENSE.rdoc b/oa-facebook/LICENSE.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-facebook/README.rdoc b/oa-facebook/README.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-facebook/Rakefile b/oa-facebook/Rakefile new file mode 100644 index 0000000..8e74b34 --- /dev/null +++ b/oa-facebook/Rakefile @@ -0,0 +1,13 @@ +require 'rubygems' +require 'rake' + +require 'mg' +MG.new('oa-facebook.gemspec') + +require 'spec/rake/spectask' +Spec::Rake::SpecTask.new(:spec) do |spec| + spec.libs << '../oa-core/lib' << 'lib' << 'spec' + spec.spec_files = FileList['spec/**/*_spec.rb'] +end + +task :default => :spec \ No newline at end of file diff --git a/oa-facebook/VERSION b/oa-facebook/VERSION new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/oa-facebook/VERSION @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file diff --git a/oa-facebook/lib/omniauth/facebook.rb b/oa-facebook/lib/omniauth/facebook.rb new file mode 100644 index 0000000..00327bb --- /dev/null +++ b/oa-facebook/lib/omniauth/facebook.rb @@ -0,0 +1,3 @@ +require 'omniauth/core' +require 'omniauth/strategies/facebook' +require 'mini_fb' \ No newline at end of file diff --git a/oa-facebook/lib/omniauth/strategies/facebook.rb b/oa-facebook/lib/omniauth/strategies/facebook.rb new file mode 100644 index 0000000..1f1f72f --- /dev/null +++ b/oa-facebook/lib/omniauth/strategies/facebook.rb @@ -0,0 +1,128 @@ +module OmniAuth + module Strategies + # An Authentication strategy that utilizes Facebook Connect. + class Facebook + include OmniAuth::Strategy + EXTENDED_PERMISSIONS = %w(publish_stream read_stream email read_mailbox offline_access create_event rsvp_event sms status_update video_upload create_note share_item) + + # Initialize the middleware. Requires a Facebook API key and secret + # and takes the following options: + # + # :permissions :: An array of Facebook extended permissions, defaults to %w(email offline_access). Use :all to include all extended permissions. + # :scripts :: A boolean value for whether or not to automatically inject the Facebook Javascripts and XD Receiver into your application. Defaults to true. + # + def initialize(app, api_key, api_secret, options = {}) + super app, :facebook + + options[:permissions] = EXTENDED_PERMISSIONS if options[:permissions] == :all + @options = { + :permissions => %w(email offline_access), + :scripts => true + }.merge(options) + + @api_key = api_key + @api_secret = api_secret + + self.extend PageInjections if @options[:scripts] + end + + def auth_hash + OmniAuth.deep_merge(super, { + 'provider' => 'facebook', + 'uid' => request.cookies["#{@api_key}_user"], + 'credentials' => { + 'key' => request.cookies["#{@api_key}_session_key"], + 'secret' => request.cookies["#{@api_key}_ss"], + 'expires' => (Time.at(request.cookies["#{@api_key}_expires"].to_i) if request.cookies["#{@api_key}_expires"].to_i > 0) + }, + 'user_info' => user_info(session_key, user_id) + }) + end + + def user_info + hash = MiniFB.call(@api_key, @api_secret, "Users.getInfo", 'uids' => request[:auth][:user_id], 'fields' => [:name, :first_name, :last_name, :username, :pic_square, :current_location])[0] + { + 'name' => user[:name], + 'first_name' => user[:first_name], + 'last_name' => user[:last_name], + 'nickname' => user[:username], + 'image' => user[:pic_square], + 'location' => user[:locale] + } + end + + def call(env) + dup = self.dup + dup.extend PageInjections if @options[:scripts] + dup.call!(env) + end + + module PageInjections + def call!(env) + super + @base_url = (request.scheme.downcase == 'https' ? 'https://ssl.connect.facebook.com' : 'http://static.ak.connect.facebook.com') + case request.path + when "/#{OmniAuth.config.path_prefix}/facebook/xd_receiver.html" + xd_receiver + else + inject_facebook + end + end + + def xd_receiver #:nodoc: + xd = <<-HTML + + + + Cross-Domain Receiver Page + + + + + + HTML + + Rack::Response.new(xd).finish + end + + def inject_facebook #:nodoc: + status, headers, responses = @app.call(@env) + responses = Array(responses) unless responses.respond_to?(:each) + + if headers["Content-Type"] =~ %r{(text/html)|(application/xhtml+xml)} + resp = [] + responses.each do |r| + r.sub! /(]*)>/i, '\1 xmlns:fb=\"http://www.facebook.com/2008/fbml\">' + r.sub! /(]*)>/i, '\1>' + r.sub! /<\/body>/i, <<-HTML + + HTML + resp << r + end + end + + Rack::Response.new(resp || responses, status, headers).finish + end + end + end + end +end \ No newline at end of file diff --git a/oa-facebook/oa-facebook.gemspec b/oa-facebook/oa-facebook.gemspec new file mode 100644 index 0000000..93a14c2 --- /dev/null +++ b/oa-facebook/oa-facebook.gemspec @@ -0,0 +1,21 @@ +version = File.open(File.dirname(__FILE__) + '/VERSION', 'r').read.strip + +Gem::Specification.new do |gem| + gem.name = "oa-facebook" + gem.version = File.open(File.dirname(__FILE__) + '/VERSION', 'r').read.strip + gem.summary = %Q{Facebook strategies for OmniAuth.} + gem.description = %Q{Facebook strategies for OmniAuth.} + gem.email = "michael@intridea.com" + gem.homepage = "http://github.com/intridea/omniauth" + gem.authors = ["Michael Bleigh"] + + gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) + + gem.add_dependency 'oa-core', "~> #{version.gsub(/\d$/,'0')}" + gem.add_dependency 'fb_mini' + + gem.add_development_dependency "rspec", ">= 1.2.9" + gem.add_development_dependency "webmock" + gem.add_development_dependency "rack-test" + gem.add_development_dependency "mg" +end \ No newline at end of file diff --git a/oa-facebook/spec/spec_helper.rb b/oa-facebook/spec/spec_helper.rb new file mode 100644 index 0000000..e69de29 diff --git a/oa-oauth/oa-oauth.gemspec b/oa-oauth/oa-oauth.gemspec index 94fca59..ce32e11 100644 --- a/oa-oauth/oa-oauth.gemspec +++ b/oa-oauth/oa-oauth.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |gem| gem.summary = %Q{OAuth strategies for OmniAuth.} gem.description = %Q{OAuth strategies for OmniAuth.} gem.email = "michael@intridea.com" - gem.homepage = "http://github.com/intridea/omni_auth" + gem.homepage = "http://github.com/intridea/omniauth" gem.authors = ["Michael Bleigh"] gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) diff --git a/oa-openid/CHANGELOG.rdoc b/oa-openid/CHANGELOG.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-openid/LICENSE.rdoc b/oa-openid/LICENSE.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-openid/README.rdoc b/oa-openid/README.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/oa-openid/lib/omniauth/strategies/google.rb b/oa-openid/lib/omniauth/strategies/google.rb new file mode 100644 index 0000000..2bba2c7 --- /dev/null +++ b/oa-openid/lib/omniauth/strategies/google.rb @@ -0,0 +1,7 @@ +module OmniAuth + module Strategies + class Google < OmniAuth::Stratgies::OpenID + def identifier; 'https://www.google.com/accounts/o8/id' end + end + end +end \ No newline at end of file diff --git a/oa-openid/lib/omniauth/strategies/open_id.rb b/oa-openid/lib/omniauth/strategies/open_id.rb index f2b16e6..851dfaa 100644 --- a/oa-openid/lib/omniauth/strategies/open_id.rb +++ b/oa-openid/lib/omniauth/strategies/open_id.rb @@ -5,27 +5,55 @@ module OmniAuth class OpenID include OmniAuth::Strategy + AX = { + :email => 'http://axschema.org/contact/email', + :name => 'http://axschema.org/namePerson', + :nickname => 'http://axschema.org/namePerson/friendly', + :first_name => 'http://axschema.org/namePerson/first', + :last_name => 'http://axschema.org/namePerson/last', + :city => 'http://axschema.org/contact/city/home', + :state => 'http://axschema.org/contact/state/home', + :website => 'http://axschema.org/contact/web/default', + :image => 'http://axschema.org/media/image/aspect11' + } + def initialize(app, store = nil, options = {}) - super(app, :open_id) + super(app, options[:name] || :open_id) @options = options - @options[:required] ||= %w(email fullname) - @options[:optional] ||= %w(nickname dob gender postcode country language timezone) + @options[:required] ||= [AX[:email], AX[:name], 'email', 'fullname'] + @options[:optional] ||= [AX[:first_name], AX[:last_name], AX[:nickname], AX[:city], AX[:state], AX[:website], AX[:image], 'postcode', 'nickname'] @store = store end def dummy_app lambda{|env| [401, {"WWW-Authenticate" => Rack::OpenID.build_header( - :identifier => request[:identifier], - :return_to => request.url + '/callback', + :identifier => identifier, + :return_to => callback_url, :required => @options[:required], :optional => @options[:optional] )}, []]} end + def callback_url + uri = URI.parse(request.url) + uri.path += '/callback' + uri.to_s + end + + def identifier + request[:identifier] + end + def request_phase - return fail!(:missing_information) unless request[:identifier] + return fail!(:missing_information) unless identifier openid = Rack::OpenID.new(dummy_app, @store) - openid.call(env) + response = openid.call(env) + case env['rack.openid.response'] + when Rack::OpenID::MissingResponse, Rack::OpenID::TimeoutResponse + fail :connection_failed + else + response + end end def callback_phase @@ -45,19 +73,36 @@ module OmniAuth def auth_hash(response) OmniAuth::Utils.deep_merge(super(), { 'uid' => response.display_identifier, - 'user_info' => user_info(response.display_identifier, ::OpenID::SReg::Response.from_success_response(response)) + 'user_info' => user_info(response) }) end - def user_info(identifier, sreg) + def user_info(response) + sreg_user_info(response).merge(ax_user_info(response)) + end + + def sreg_user_info(response) + sreg = ::OpenID::SReg::Response.from_success_response(response) + return {} unless sreg { 'email' => sreg['email'], 'name' => sreg['fullname'], 'location' => sreg['postcode'], - 'nickname' => sreg['nickname'], - 'urls' => {'Profile' => identifier} + 'nickname' => sreg['nickname'] }.reject{|k,v| v.nil? || v == ''} end + + def ax_user_info(response) + ax = ::OpenID::AX::FetchResponse.from_success_response(response) + return {} unless ax + { + 'email' => ax[AX[:email]], + 'name' => ax[AX[:name]], + 'location' => ("#{ax[AX[:city]]}, #{ax[AX[:state]]}" if Array(ax[AX[:city]]).any? && Array(ax[AX[:state]]).any?), + 'nickname' => ax[AX[:nickname]], + 'urls' => ({'Website' => Array(ax[AX[:website]]).first} if Array(ax[AX[:website]]).any?) + }.inject({}){|h,(k,v)| h[k] = Array(v).first; h}.reject{|k,v| v.nil? || v == ''} + end end end end \ No newline at end of file diff --git a/oa-openid/oa-openid.gemspec b/oa-openid/oa-openid.gemspec index 185ab8c..125b0ba 100644 --- a/oa-openid/oa-openid.gemspec +++ b/oa-openid/oa-openid.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |gem| gem.summary = %Q{OpenID strategies for OmniAuth.} gem.description = %Q{OpenID strategies for OmniAuth.} gem.email = "michael@intridea.com" - gem.homepage = "http://github.com/intridea/omni_auth" + gem.homepage = "http://github.com/intridea/omniauth" gem.authors = ["Michael Bleigh"] gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc) diff --git a/omniauth/CHANGELOG.rdoc b/omniauth/CHANGELOG.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/omniauth/LICENSE.rdoc b/omniauth/LICENSE.rdoc new file mode 100644 index 0000000..e69de29 diff --git a/omniauth/lib/omniauth.rb b/omniauth/lib/omniauth.rb index 92918b9..c68a1a6 100644 --- a/omniauth/lib/omniauth.rb +++ b/omniauth/lib/omniauth.rb @@ -1,9 +1,5 @@ require 'omniauth/core' -%w(password oauth basic openid).each do |s| - begin - require "omniauth/#{s}" - rescue LoadError - puts "Unable to find the files for oa-#{s}, ignored it." - end +%w(password oauth basic openid facebook).each do |s| + require "omniauth/#{s}" end \ No newline at end of file diff --git a/omniauth/omniauth.gemspec b/omniauth/omniauth.gemspec index 1c21908..8339cd5 100644 --- a/omniauth/omniauth.gemspec +++ b/omniauth/omniauth.gemspec @@ -6,7 +6,7 @@ Gem::Specification.new do |gem| gem.summary = %Q{Rack middleware for standardized multi-provider authentication.} gem.description = %Q{OmniAuth is an authentication framework that that separates the concept of authentiation from the concept of identity, providing simple hooks for any application to have one or multiple authentication providers for a user.} gem.email = "michael@intridea.com" - gem.homepage = "http://github.com/intridea/omni_auth" + gem.homepage = "http://github.com/intridea/omniauth" gem.authors = ["Michael Bleigh"] gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE.rdoc CHANGELOG.rdoc)