diff --git a/.gitignore b/.gitignore index 654ce3f..16c7768 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ oa-live .bundle .project .loadpath +.yardoc +doc + +Gemfile.lock diff --git a/README.markdown b/README.markdown index f361c32..f3b464f 100644 --- a/README.markdown +++ b/README.markdown @@ -22,10 +22,12 @@ OmniAuth currently supports the following external providers: * Foursquare * LinkedIn * GitHub + * Identi.ca (credit: [dcu](http://github.com/dcu)) + * Gowalla (credit: [kvnsmth](http://github.com/kvnsmth)) * OpenID * Google Apps (via OpenID) -* CAS (Central Authentication Service) -* LDAP +* CAS (Central Authentication Service) (credit: [jamesarosen](http://github.com/jamesarosen)) +* LDAP (credit: **Ping Yu**) ## Usage @@ -34,10 +36,10 @@ OmniAuth is a collection of Rack middleware. To use a single strategy, you simpl require 'oa-oauth' use OmniAuth::Strategies::Twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET' -Now to initiate authentication you merely need to redirect the user to `/auth/twitter` via a link or other means. Once the user has authenticated to Twitter, they will be redirected to `/auth/twitter/callback`. You should build an endpoint that handles this URL, at which point you will will have access to the authentication information through the `rack.auth` parameter of the Rack environment. For example, in Sinatra you would do something like this: +Now to initiate authentication you merely need to redirect the user to `/auth/twitter` via a link or other means. Once the user has authenticated to Twitter, they will be redirected to `/auth/twitter/callback`. You should build an endpoint that handles this URL, at which point you will will have access to the authentication information through the `omniauth.auth` parameter of the Rack environment. For example, in Sinatra you would do something like this: get '/auth/twitter/callback' do - auth_hash = request.env['rack.auth'] + auth_hash = request.env['omniauth.auth'] end The hash in question will look something like this: diff --git a/Rakefile b/Rakefile index 43a2060..836de24 100644 --- a/Rakefile +++ b/Rakefile @@ -1,8 +1,14 @@ require 'rubygems' require 'rake' -require 'term/ansicolor' -include Term::ANSIColor +begin + require 'term/ansicolor' + include Term::ANSIColor +rescue LoadError + def cyan; '' end + def blue; '' end + def clear; '' end +end OMNIAUTH_GEMS = %w(oa-basic oa-core oa-oauth oa-openid oa-enterprise omniauth) @@ -60,6 +66,15 @@ namespace :dependencies do end end +task :release => ['release:tag', 'gems:publish', 'doc:pages:publish'] + +namespace :release do + task :tag do + system("git tag v#{version}") + system('git push origin --tags') + end +end + namespace :gems do desc 'Build all gems' @@ -70,7 +85,7 @@ namespace :gems do end desc 'Push all gems to Gemcutter' - task :release do + task :push do each_gem('is releasing to Gemcutter...') do system('rake gem:publish') end @@ -119,3 +134,33 @@ namespace :version do end task :default => :spec + +begin + YARD_OPTS = ['-m', 'markdown', '-M', 'maruku'] + require 'yard' + YARD::Rake::YardocTask.new(:doc) do |t| + t.files = OMNIAUTH_GEMS.inject([]){|a,g| a = a + ["#{g}/lib/**/*.rb"]; a} + ['README.markdown'] + t.options = YARD_OPTS + end + + namespace :doc do + YARD::Rake::YardocTask.new(:pages) do |t| + t.files = OMNIAUTH_GEMS.inject([]){|a,g| a = a + ["#{g}/lib/**/*.rb"]; a} + ['README.markdown'] + t.options = YARD_OPTS + ['-o', '../omniauth.doc'] + end + + namespace :pages do + desc 'Generate and publish YARD docs to GitHub pages.' + task :publish => ['doc:pages'] do + Dir.chdir(File.dirname(__FILE__) + '/../omniauth.doc') do + system("git add .") + system("git add -u") + system("git commit -m 'Generating docs for version #{version}.'") + system("git push origin gh-pages") + end + end + end + end +rescue LoadError + puts "You need to install YARD." +end \ No newline at end of file diff --git a/VERSION b/VERSION index 6da28dd..446ba66 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 \ No newline at end of file +0.1.4 \ No newline at end of file diff --git a/oa-basic/Gemfile.lock b/oa-basic/Gemfile.lock deleted file mode 100644 index 2b2c88f..0000000 --- a/oa-basic/Gemfile.lock +++ /dev/null @@ -1,50 +0,0 @@ -PATH - remote: . - specs: - oa-basic (0.1.1) - multi_json (~> 0.0.2) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - rest-client (~> 1.6.0) - -PATH - remote: /Users/mbleigh/gems/omniauth/oa-core - specs: - oa-core (0.1.1) - rack (~> 1.1) - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - json (1.4.3) - mg (0.0.8) - rake - mime-types (1.16) - multi_json (0.0.4) - nokogiri (1.4.3.1) - rack (1.2.1) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rest-client (1.6.0) - mime-types (>= 1.16) - rspec (1.3.0) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-basic! - oa-core! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - webmock (~> 1.3.4) diff --git a/oa-basic/lib/omniauth/strategies/http_basic.rb b/oa-basic/lib/omniauth/strategies/http_basic.rb index fa82c1f..61dd32b 100644 --- a/oa-basic/lib/omniauth/strategies/http_basic.rb +++ b/oa-basic/lib/omniauth/strategies/http_basic.rb @@ -35,13 +35,13 @@ module OmniAuth def perform @response = perform_authentication(endpoint) - request.POST['auth'] = auth_hash + @env['omniauth.auth'] = auth_hash @env['REQUEST_METHOD'] = 'GET' @env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback" - - @app.call(@env) - rescue RestClient::Request::Unauthorized - fail!(:invalid_credentials) + + call_app! + rescue RestClient::Request::Unauthorized => e + fail!(:invalid_credentials, e) end def perform_authentication(uri, headers = request_headers) diff --git a/oa-core/Gemfile.lock b/oa-core/Gemfile.lock deleted file mode 100644 index 555cdea..0000000 --- a/oa-core/Gemfile.lock +++ /dev/null @@ -1,35 +0,0 @@ -PATH - remote: . - specs: - oa-core (0.1.1) - rack (~> 1.1) - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - json (1.4.3) - mg (0.0.8) - rake - rack (1.2.1) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rspec (1.3.0) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-core! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - webmock (~> 1.3.4) diff --git a/oa-core/lib/omniauth/builder.rb b/oa-core/lib/omniauth/builder.rb index 89907b4..9be8861 100644 --- a/oa-core/lib/omniauth/builder.rb +++ b/oa-core/lib/omniauth/builder.rb @@ -7,6 +7,14 @@ module OmniAuth super(&block) end + def on_failure(&block) + OmniAuth.config.on_failure = block + end + + def configure(&block) + OmniAuth.configure(&block) + end + def provider(klass, *args, &block) if klass.is_a?(Class) middleware = klass diff --git a/oa-core/lib/omniauth/core.rb b/oa-core/lib/omniauth/core.rb index 4dacde6..5f2f71e 100644 --- a/oa-core/lib/omniauth/core.rb +++ b/oa-core/lib/omniauth/core.rb @@ -3,35 +3,35 @@ require 'singleton' require 'omniauth/form' module OmniAuth - + autoload :Builder, 'omniauth/builder' autoload :Strategy, 'omniauth/strategy' autoload :Test, 'omniauth/test' - + module Strategies autoload :Password, 'omniauth/strategies/password' end - + class Configuration include Singleton - + @@defaults = { :path_prefix => '/auth', :on_failure => Proc.new do |env, message_key| new_path = "#{OmniAuth.config.path_prefix}/failure?message=#{message_key}" - [302, {'Location' => "#{new_path}"}, []] + [302, {'Location' => "#{new_path}", 'Content-Type'=> 'text/html'}, []] end, :form_css => Form::DEFAULT_CSS } - + def self.defaults @@defaults end - + def initialize @@defaults.each_pair{|k,v| self.send("#{k}=",v)} end - + def on_failure(&block) if block_given? @on_failure = block @@ -39,19 +39,19 @@ module OmniAuth @on_failure end end - + attr_writer :on_failure attr_accessor :path_prefix, :form_css end - + def self.config Configuration.instance end - + def self.configure yield config end - + module Utils CAMELIZE_SPECIAL = { 'oauth' => 'OAuth', @@ -60,31 +60,31 @@ module OmniAuth 'open_id' => 'OpenID', 'github' => 'GitHub' } - + module_function - + def form_css "" end - + def deep_merge(hash, other_hash) target = hash.dup - + other_hash.keys.each do |key| if other_hash[key].is_a? ::Hash and hash[key].is_a? ::Hash target[key] = deep_merge(target[key],other_hash[key]) next end - + target[key] = other_hash[key] end - + target end - + def camelize(word, first_letter_in_uppercase = true) return CAMELIZE_SPECIAL[word.to_s] if CAMELIZE_SPECIAL[word.to_s] - + if first_letter_in_uppercase word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } else diff --git a/oa-core/lib/omniauth/strategies/password.rb b/oa-core/lib/omniauth/strategies/password.rb index 903313c..01ab9c1 100644 --- a/oa-core/lib/omniauth/strategies/password.rb +++ b/oa-core/lib/omniauth/strategies/password.rb @@ -19,8 +19,8 @@ module OmniAuth return fail!(:password_mismatch) if request[:password_confirmation] && request[:password_confirmation] != '' && request[:password] != request[:password_confirmation] env['REQUEST_METHOD'] = 'GET' env['PATH_INFO'] = request.path + '/callback' - request['auth'] = auth_hash(encrypt(request[:identifier], request[:password])) - @app.call(env) + env['omniauth.auth'] = auth_hash(encrypt(request[:identifier], request[:password])) + call_app! end def auth_hash(crypted_password) @@ -33,7 +33,7 @@ module OmniAuth end def callback_phase - @app.call(env) + call_app! end def encrypt(identifier, password) diff --git a/oa-core/lib/omniauth/strategy.rb b/oa-core/lib/omniauth/strategy.rb index 6d74f39..50fc880 100644 --- a/oa-core/lib/omniauth/strategy.rb +++ b/oa-core/lib/omniauth/strategy.rb @@ -29,7 +29,7 @@ module OmniAuth if respond_to?(:other_phase) other_phase else - @app.call(env) + call_app! end end end @@ -39,8 +39,15 @@ module OmniAuth end def callback_phase - env['rack.auth'] = auth_hash - @app.call(env) + @env['omniauth.auth'] = auth_hash + call_app! + end + + def call_app! + @env['rack.auth'] = env['omniauth.auth'] if env.key?('omniauth.auth') + @env['rack.auth.error'] = env['omniauth.error'] if env.key?('omniauth.error') + + @app.call(@env) end def auth_hash @@ -77,7 +84,8 @@ module OmniAuth def user_info; {} end - def fail!(message_key) + def fail!(message_key, exception = nil) + self.env['omniauth.error'] = exception OmniAuth.config.on_failure.call(self.env, message_key.to_sym) end end diff --git a/oa-core/lib/omniauth/test/strategy_macros.rb b/oa-core/lib/omniauth/test/strategy_macros.rb index 54fc153..7b56d23 100644 --- a/oa-core/lib/omniauth/test/strategy_macros.rb +++ b/oa-core/lib/omniauth/test/strategy_macros.rb @@ -6,19 +6,19 @@ module OmniAuth def sets_an_auth_hash it 'should set an auth hash' do - last_request['auth'].should be_kind_of(Hash) + last_request.env['omniauth.auth'].should be_kind_of(Hash) end end def sets_provider_to(provider) it "should set the provider to #{provider}" do - (last_request['auth'] || {})['provider'].should == provider + (last_request.env['omniauth.auth'] || {})['provider'].should == provider end end def sets_uid_to(uid) it "should set the UID to #{uid}" do - (last_request['auth'] || {})['uid'].should == uid + (last_request.env['omniauth.auth'] || {})['uid'].should == uid end end diff --git a/oa-core/lib/omniauth/test/strategy_test_case.rb b/oa-core/lib/omniauth/test/strategy_test_case.rb index 24429ba..48b8beb 100644 --- a/oa-core/lib/omniauth/test/strategy_test_case.rb +++ b/oa-core/lib/omniauth/test/strategy_test_case.rb @@ -5,8 +5,9 @@ module OmniAuth module Test - # Support for testing OmniAuth strategies. Usage: + # Support for testing OmniAuth strategies. # + # @example Usage # class MyStrategyTest < Test::Unit::TestCase # include OmniAuth::Test::StrategyTestCase # def strategy diff --git a/oa-core/spec/omniauth/strategies/password_spec.rb b/oa-core/spec/omniauth/strategies/password_spec.rb index 49e365f..b5158ca 100644 --- a/oa-core/spec/omniauth/strategies/password_spec.rb +++ b/oa-core/spec/omniauth/strategies/password_spec.rb @@ -29,7 +29,7 @@ describe OmniAuth::Strategies::Password, :type => :strategy do sets_an_auth_hash sets_provider_to 'password' it 'should set the UID to an opaque identifier' do - uid = last_request['auth']['uid'] + uid = last_request.env['omniauth.auth']['uid'] uid.should_not be_nil uid.should_not =~ /jerome/ uid.should_not =~ /my password/ diff --git a/oa-enterprise/Gemfile.lock b/oa-enterprise/Gemfile.lock deleted file mode 100644 index 869d769..0000000 --- a/oa-enterprise/Gemfile.lock +++ /dev/null @@ -1,48 +0,0 @@ -PATH - remote: /Users/mbleigh/gems/omniauth/oa-core - specs: - oa-core (0.1.1) - rack (~> 1.1) - -PATH - remote: . - specs: - oa-enterprise (0.1.1) - net-ldap (~> 0.1.1) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - rubyntlm (~> 0.1.1) - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - json (1.4.3) - mg (0.0.8) - rake - net-ldap (0.1.1) - nokogiri (1.4.3.1) - rack (1.1.0) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rspec (1.3.0) - rubyntlm (0.1.1) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-core! - oa-enterprise! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - webmock (~> 1.3.4) diff --git a/oa-enterprise/lib/omniauth/strategies/cas/configuration.rb b/oa-enterprise/lib/omniauth/strategies/cas/configuration.rb index 5aa2459..8414a38 100644 --- a/oa-enterprise/lib/omniauth/strategies/cas/configuration.rb +++ b/oa-enterprise/lib/omniauth/strategies/cas/configuration.rb @@ -11,12 +11,12 @@ module OmniAuth # @param [Hash] params configuration options # @option params [String, nil] :cas_server the CAS server root URL; probably something like - # 'http://cas.mycompany.com' or 'http://cas.mycompany.com/cas'; optional. + # `http://cas.mycompany.com` or `http://cas.mycompany.com/cas`; optional. # @option params [String, nil] :cas_login_url (:cas_server + '/login') the URL to which to - # redirect for logins; options if :cas_server is specified, + # redirect for logins; options if `:cas_server` is specified, # required otherwise. # @option params [String, nil] :cas_service_validate_url (:cas_server + '/serviceValidate') the - # URL to use for validating service tickets; optional if :cas_server is + # URL to use for validating service tickets; optional if `:cas_server` is # specified, requred otherwise. def initialize(params) parse_params params @@ -26,8 +26,7 @@ module OmniAuth # # @param [String] service the service (a.k.a. return-to) URL # - # @return [String] a URL like - # "http://cas.mycompany.com/login?service=..." + # @return [String] a URL like `http://cas.mycompany.com/login?service=...` def login_url(service) append_service @login_url, service end @@ -37,8 +36,7 @@ module OmniAuth # @param [String] service the service (a.k.a. return-to) URL # @param [String] ticket the ticket to validate # - # @return [String] a URL like - # "http://cas.mycompany.com/serviceValidate?service=...&ticket=..." + # @return [String] a URL like `http://cas.mycompany.com/serviceValidate?service=...&ticket=...` def service_validate_url(service, ticket) url = append_service @service_validate_url, service url << '&ticket=' << Rack::Utils.escape(ticket) diff --git a/oa-enterprise/lib/omniauth/strategies/cas/service_ticket_validator.rb b/oa-enterprise/lib/omniauth/strategies/cas/service_ticket_validator.rb index 69b1326..a3fd001 100644 --- a/oa-enterprise/lib/omniauth/strategies/cas/service_ticket_validator.rb +++ b/oa-enterprise/lib/omniauth/strategies/cas/service_ticket_validator.rb @@ -31,7 +31,7 @@ module OmniAuth private - # turns an node into a Hash; + # turns an `` node into a Hash; # returns nil if given nil def parse_user_info(node) return nil if node.nil? @@ -45,8 +45,8 @@ module OmniAuth end end - # finds an node in - # a body if present; returns nil + # finds an `` node in + # a `` body if present; returns nil # if the passed body is nil or if there is no such node. def find_authentication_success(body) return nil if body.nil? || body == '' @@ -62,7 +62,7 @@ module OmniAuth end end - # retrieves the XML from the CAS server + # retrieves the `` XML from the CAS server def get_service_response_body result = '' http = Net::HTTP.new(@uri.host, @uri.port) diff --git a/oa-enterprise/lib/omniauth/strategies/ldap.rb b/oa-enterprise/lib/omniauth/strategies/ldap.rb index b6688a6..84087e3 100644 --- a/oa-enterprise/lib/omniauth/strategies/ldap.rb +++ b/oa-enterprise/lib/omniauth/strategies/ldap.rb @@ -45,14 +45,12 @@ module OmniAuth @adaptor.bind(:bind_dn => request.POST['username'], :password => request.POST['password']) @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, request.POST['username']),:limit => 1) @user_info = self.class.map_user(@@config, @ldap_user_info) - request.POST['auth'] = auth_hash @env['REQUEST_METHOD'] = 'GET' @env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback" - @app.call(@env) + call_app! rescue Exception => e - puts e.message - fail!(:invalid_credentials) + fail!(:invalid_credentials, e) end end diff --git a/oa-oauth/Gemfile.lock b/oa-oauth/Gemfile.lock deleted file mode 100644 index 1a7c514..0000000 --- a/oa-oauth/Gemfile.lock +++ /dev/null @@ -1,55 +0,0 @@ -PATH - remote: /Users/mbleigh/gems/omniauth/oa-core - specs: - oa-core (0.1.1) - rack (~> 1.1) - -PATH - remote: . - specs: - oa-oauth (0.1.1) - multi_json (~> 0.0.2) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - oauth (~> 0.4.0) - oauth2 (~> 0.0.10) - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - faraday (0.4.6) - addressable (>= 2.1.1) - rack (>= 1.0.1) - json (1.4.3) - mg (0.0.8) - rake - multi_json (0.0.4) - nokogiri (1.4.3.1) - oauth (0.4.3) - oauth2 (0.0.13) - faraday (~> 0.4.1) - multi_json (>= 0.0.4) - rack (1.2.1) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rspec (1.3.0) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-core! - oa-oauth! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - webmock (~> 1.3.4) diff --git a/oa-oauth/lib/omniauth/oauth.rb b/oa-oauth/lib/omniauth/oauth.rb index 7195a76..a6d2ea4 100644 --- a/oa-oauth/lib/omniauth/oauth.rb +++ b/oa-oauth/lib/omniauth/oauth.rb @@ -11,5 +11,7 @@ module OmniAuth autoload :GitHub, 'omniauth/strategies/github' autoload :ThirtySevenSignals, 'omniauth/strategies/thirty_seven_signals' autoload :Foursquare, 'omniauth/strategies/foursquare' + autoload :Gowalla, 'omniauth/strategies/gowalla' + autoload :Identica, 'omniauth/strategies/identica' end end diff --git a/oa-oauth/lib/omniauth/strategies/facebook.rb b/oa-oauth/lib/omniauth/strategies/facebook.rb index 2130646..fafbcd9 100644 --- a/oa-oauth/lib/omniauth/strategies/facebook.rb +++ b/oa-oauth/lib/omniauth/strategies/facebook.rb @@ -3,18 +3,16 @@ require 'multi_json' module OmniAuth module Strategies - # # Authenticate to Facebook utilizing OAuth 2.0 and retrieve # basic user information. # - # Usage: - # - # use OmniAuth::Strategies::Facebook, 'app_id', 'app_secret' - # - # Options: - # - # :scope :: Extended permissions such as email and offline_access (which are the defaults). + # @example Basic Usage + # use OmniAuth::Strategies::Facebook, 'app_id', 'app_secret' class Facebook < OAuth2 + # @param [Rack Application] app standard middleware application parameter + # @param [String] app_id the application id as [registered on Facebook](http://www.facebook.com/developers/) + # @param [String] app_secret the application secret as registered on Facebook + # @option options [String] :scope ('email,offline_access') comma-separated extended permissions such as `email` and `manage_pages` def initialize(app, app_id, app_secret, options = {}) options[:site] = 'https://graph.facebook.com/' super(app, :facebook, app_id, app_secret, options) @@ -24,9 +22,9 @@ module OmniAuth @data ||= MultiJson.decode(@access_token.get('/me')) end - def request_phase(options = {}) + def request_phase options[:scope] ||= "email,offline_access" - super(options) + super end def user_info diff --git a/oa-oauth/lib/omniauth/strategies/github.rb b/oa-oauth/lib/omniauth/strategies/github.rb index a20f79c..e2d0d8e 100644 --- a/oa-oauth/lib/omniauth/strategies/github.rb +++ b/oa-oauth/lib/omniauth/strategies/github.rb @@ -3,7 +3,13 @@ require 'multi_json' module OmniAuth module Strategies + # OAuth 2.0 based authentication with GitHub. In order to + # sign up for an application, you need to [register an application](http://github.com/account/applications/new) + # and provide the proper credentials to this middleware. class GitHub < OAuth2 + # @param [Rack Application] app standard middleware application argument + # @param [String] app_id the application ID for your client + # @param [String] app_secret the application secret def initialize(app, app_id, app_secret, options = {}) options[:site] = 'https://github.com/' options[:authorize_path] = '/login/oauth/authorize' @@ -11,6 +17,8 @@ module OmniAuth super(app, :github, app_id, app_secret, options) end + protected + def user_data @data ||= MultiJson.decode(@access_token.get('/api/v2/json/user/show'))['user'] end diff --git a/oa-oauth/lib/omniauth/strategies/gowalla.rb b/oa-oauth/lib/omniauth/strategies/gowalla.rb new file mode 100644 index 0000000..a8f3a6d --- /dev/null +++ b/oa-oauth/lib/omniauth/strategies/gowalla.rb @@ -0,0 +1,61 @@ +require 'omniauth/oauth' +require 'multi_json' + +module OmniAuth + module Strategies + # + # Authenticate to Gowalla utilizing OAuth 2.0 and retrieve + # basic user information. + # + # @example Basic Usage + # use OmniAuth::Strategies::Gowalla, 'API Key', 'Secret Key' + class Gowalla < OAuth2 + # @param [Rack Application] app standard middleware application parameter + # @param [String] api_key the application id as [registered on Gowalla](http://gowalla.com/api/keys) + # @param [String] secret_key the application secret as [registered on Gowalla](http://gowalla.com/api/keys) + # @option options ['read','read-write'] :scope ('read') the scope of your authorization request; must be `read` or `read-write` + def initialize(app, api_key, secret_key, options = {}) + options[:site] = 'https://api.gowalla.com/api/oauth' + options[:authorize_url] = 'https://gowalla.com/api/oauth/new' + options[:access_token_url] = 'https://api.gowalla.com/api/oauth/token' + super(app, :gowalla, api_key, secret_key, options) + end + + protected + + def user_data + @data ||= MultiJson.decode(@access_token.get("/users/me.json")) + end + + def request_phase + options[:scope] ||= "read" + super + end + + def user_info + { + 'name' => "#{user_data['first_name']} #{user_data['last_name']}", + 'nickname' => user_data["username"], + 'first_name' => user_data["first_name"], + 'last_name' => user_data["last_name"], + 'location' => user_data["hometown"], + 'description' => user_data["bio"], + 'image' => user_data["image_url"], + 'phone' => nil, + 'urls' => { + 'Gowalla' => "http://www.gowalla.com#{user_data['url']}", + 'Website' => user_data["website"] + } + } + end + + def auth_hash + OmniAuth::Utils.deep_merge(super, { + 'uid' => user_data["url"].split('/').last, + 'user_info' => user_info, + 'extra' => {'user_hash' => user_data} + }) + end + end + end +end diff --git a/oa-oauth/lib/omniauth/strategies/identica.rb b/oa-oauth/lib/omniauth/strategies/identica.rb new file mode 100644 index 0000000..b038cfd --- /dev/null +++ b/oa-oauth/lib/omniauth/strategies/identica.rb @@ -0,0 +1,49 @@ +require 'omniauth/oauth' +require 'multi_json' + +module OmniAuth + module Strategies + # + # Authenticate to Identica via OAuth and retrieve basic + # user information. + # + # Usage: + # + # use OmniAuth::Strategies::Identica, 'consumerkey', 'consumersecret' + # + class Identica < OmniAuth::Strategies::OAuth + def initialize(app, consumer_key, consumer_secret) + super(app, :identica, consumer_key, consumer_secret, + :site => 'http://identi.ca', + :request_token_path => "/api/oauth/request_token", + :access_token_path => "/api/oauth/access_token", + :authorize_path => "/api/oauth/authorize") + end + + def auth_hash + OmniAuth::Utils.deep_merge(super, { + 'uid' => @access_token.params[:user_id], + 'user_info' => user_info, + 'extra' => {'user_hash' => user_hash} + }) + end + + def user_info + user_hash = self.user_hash + + { + 'nickname' => user_hash['screen_name'], + 'name' => user_hash['name'], + 'location' => user_hash['location'], + 'image' => user_hash['profile_image_url'], + 'description' => user_hash['description'], + 'urls' => {'Website' => user_hash['url']} + } + end + + def user_hash + @user_hash ||= MultiJson.decode(@access_token.get('/api/account/verify_credentials.json').body) + end + end + end +end diff --git a/oa-oauth/lib/omniauth/strategies/oauth.rb b/oa-oauth/lib/omniauth/strategies/oauth.rb index 08b5622..d8e6b05 100644 --- a/oa-oauth/lib/omniauth/strategies/oauth.rb +++ b/oa-oauth/lib/omniauth/strategies/oauth.rb @@ -23,12 +23,9 @@ module OmniAuth def callback_phase request_token = ::OAuth::RequestToken.new(consumer, session[:oauth][name.to_sym].delete(:request_token), session[:oauth][name.to_sym].delete(:request_secret)) @access_token = request_token.get_access_token(:oauth_verifier => request.params['oauth_verifier']) - - request['auth'] = self.auth_hash - - @app.call(self.env) - rescue ::OAuth::Unauthorized - fail!(:invalid_credentials) + super + rescue ::OAuth::Unauthorized => e + fail!(:invalid_credentials, e) end def auth_hash diff --git a/oa-oauth/lib/omniauth/strategies/oauth2.rb b/oa-oauth/lib/omniauth/strategies/oauth2.rb index 1084f6a..ba79d2f 100644 --- a/oa-oauth/lib/omniauth/strategies/oauth2.rb +++ b/oa-oauth/lib/omniauth/strategies/oauth2.rb @@ -5,29 +5,60 @@ require 'omniauth/oauth' module OmniAuth module Strategies + # Authentication strategy for connecting with APIs constructed using + # the [OAuth 2.0 Specification](http://tools.ietf.org/html/draft-ietf-oauth-v2-10). + # You must generally register your application with the provider and + # utilize an application id and secret in order to authenticate using + # OAuth 2.0. class OAuth2 include OmniAuth::Strategy + # The options passed in to the strategy. + attr_accessor :options + # The `OAuth2::Client` for this strategy. + attr_accessor :client + + # An error that is indicated in the OAuth 2.0 callback. + # This could be a `redirect_uri_mismatch` or other + class CallbackError < StandardError + attr_accessor :error, :error_reason, :error_uri + + def initialize(error, error_reason=nil, error_uri=nil) + self.error = error + self.error_reason = error_reason + self.error_uri = error_uri + end + end + + # Initialize a new OAuth 2.0 authentication provider. + + # @param [Rack Application] app standard middleware application argument + # @param [String] name the name for this provider to be used in its URL, e.g. `/auth/name` + # @param [String] client_id the client/application ID of this provider + # @param [String] client_secret the client/application secret of this provider + # @param [Hash] options that will be passed through to the OAuth2::Client (see [oauth2 docs](http://rubydoc.info/gems/oauth2)) def initialize(app, name, client_id, client_secret, options = {}) super(app, name) - @options = options - @client = ::OAuth2::Client.new(client_id, client_secret, options) + self.options = options + self.client = ::OAuth2::Client.new(client_id, client_secret, options) end protected - - attr_accessor :client - - def request_phase(options = {}) + + def request_phase redirect client.web_server.authorize_url({:redirect_uri => callback_url}.merge(options)) end def callback_phase + if request.params['error'] + raise CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri']) + end + verifier = request.params['code'] @access_token = client.web_server.get_access_token(verifier, :redirect_uri => callback_url) super - rescue ::OAuth2::HTTPError => e - fail!(:invalid_credentials) + rescue ::OAuth2::HTTPError, ::OAuth2::AccessDenied, CallbackError => e + fail!(:invalid_credentials, e) end def auth_hash diff --git a/oa-oauth/lib/omniauth/strategies/twitter.rb b/oa-oauth/lib/omniauth/strategies/twitter.rb index 097d350..7172792 100644 --- a/oa-oauth/lib/omniauth/strategies/twitter.rb +++ b/oa-oauth/lib/omniauth/strategies/twitter.rb @@ -14,7 +14,8 @@ module OmniAuth class Twitter < OmniAuth::Strategies::OAuth def initialize(app, consumer_key, consumer_secret) super(app, :twitter, consumer_key, consumer_secret, - :site => 'https://api.twitter.com') + :site => 'https://api.twitter.com', + :authorize_path => '/oauth/authenticate') end def auth_hash diff --git a/oa-oauth/spec/omniauth/strategies/gowalla_spec.rb b/oa-oauth/spec/omniauth/strategies/gowalla_spec.rb new file mode 100644 index 0000000..f6c5cb9 --- /dev/null +++ b/oa-oauth/spec/omniauth/strategies/gowalla_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +describe OmniAuth::Strategies::Gowalla do + + it 'should subclass OAuth2' do + OmniAuth::Strategies::Gowalla.should < OmniAuth::Strategies::OAuth2 + end + + it 'should initialize with just api key and secret key' do + lambda{OmniAuth::Strategies::Gowalla.new({},'api_key','secret_key')}.should_not raise_error + end + +end \ No newline at end of file diff --git a/oa-oauth/spec/omniauth/strategies/identica_spec.rb b/oa-oauth/spec/omniauth/strategies/identica_spec.rb new file mode 100644 index 0000000..a9a0620 --- /dev/null +++ b/oa-oauth/spec/omniauth/strategies/identica_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +describe 'OmniAuth::Strategies::Identica' do + + it 'should subclass Identica' do + OmniAuth::Strategies::Identica.should < OmniAuth::Strategies::OAuth + end + + it 'should initialize with just consumer key and secret' do + lambda{OmniAuth::Strategies::Identica.new({},'abc','def')}.should_not raise_error + end + +end diff --git a/oa-oauth/spec/omniauth/strategies/oauth_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth_spec.rb index 0cef19b..6d6b1fa 100644 --- a/oa-oauth/spec/omniauth/strategies/oauth_spec.rb +++ b/oa-oauth/spec/omniauth/strategies/oauth_spec.rb @@ -8,7 +8,7 @@ describe "OmniAuth::Strategies::OAuth" do use OmniAuth::Builder do provider :oauth, 'example.org', 'abc', 'def', :site => 'https://api.example.org' end - run lambda { |env| [200, {'Content-Type' => 'text/plain'}, [Rack::Request.new(env).params.key?('auth').to_s]] } + run lambda { |env| [200, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] } }.to_app end @@ -43,8 +43,8 @@ describe "OmniAuth::Strategies::OAuth" do end it 'should exchange the request token for an access token' do - last_request['auth']['provider'].should == 'example.org' - last_request['auth']['extra']['access_token'].should be_kind_of(OAuth::AccessToken) + last_request.env['omniauth.auth']['provider'].should == 'example.org' + last_request.env['omniauth.auth']['extra']['access_token'].should be_kind_of(OAuth::AccessToken) end it 'should call through to the master app' do diff --git a/oa-openid/Gemfile.lock b/oa-openid/Gemfile.lock deleted file mode 100644 index 048f938..0000000 --- a/oa-openid/Gemfile.lock +++ /dev/null @@ -1,51 +0,0 @@ -PATH - remote: /Users/mbleigh/gems/omniauth/oa-core - specs: - oa-core (0.1.1) - rack (~> 1.1) - -PATH - remote: . - specs: - oa-openid (0.1.1) - oa-core (= 0.1.1) - rack-openid (~> 1.1.1) - ruby-openid-apps-discovery - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - json (1.4.3) - mg (0.0.8) - rake - rack (1.2.1) - rack-openid (1.1.1) - rack (>= 0.4) - ruby-openid (>= 2.0.3) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rspec (1.3.0) - ruby-openid (2.1.8) - ruby-openid-apps-discovery (1.2.0) - ruby-openid (>= 2.1.7) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-core! - oa-openid! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - ruby-openid-apps-discovery - webmock (~> 1.3.4) diff --git a/oa-openid/README.rdoc b/oa-openid/README.rdoc deleted file mode 100644 index 0327f0a..0000000 --- a/oa-openid/README.rdoc +++ /dev/null @@ -1,37 +0,0 @@ -= OmniAuth::OpenID - -OpenID strategies for the OmniAuth gem. - -== Installation - -To get just OpenID functionality: - - gem install oa-openid - -For the full auth suite: - - gem install omniauth - -== Stand-Alone Example - -Use the strategy as a middleware in your application: - - require 'omniauth/openid' - require 'openid/store/filesystem' - - use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('/tmp') - -Then simply direct users to '/auth/open_id' to prompt them for their OpenID identifier. You may also pre-set the identifier by passing an identifier parameter to the URL (Example: /auth/open_id?identifier=google.com). - -== OmniAuth Builder - -If OpenID is one of several authentication strategies, use the OmniAuth Builder: - - require 'omniauth/openid' - require 'omniauth/basic' # for Campfire - require 'openid/store/filesystem' - - use OmniAuth::Builder do - provider :open_id, OpenID::Store::Filesystem.new('/tmp') - provider :campfire - end diff --git a/oa-openid/lib/omniauth/openid.rb b/oa-openid/lib/omniauth/openid.rb index 5a40f8c..515b250 100644 --- a/oa-openid/lib/omniauth/openid.rb +++ b/oa-openid/lib/omniauth/openid.rb @@ -1,6 +1,57 @@ require 'omniauth/core' module OmniAuth + # OmniAuth::OpenID provides strategies for authenticating to providers + # using the OpenID standard. + # + # # Installation + # + # To get just OpenID functionality: + # + # gem install oa-openid + # + # For the full auth suite: + # + # gem install omniauth + # + # # Stand-Alone Example + # + # Use the strategy as a middleware in your application: + # + # require 'omniauth/openid' + # require 'openid/store/filesystem' + # + # use Rack::Session::Cookie + # use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('/tmp') + # + # Then simply direct users to '/auth/open_id' to prompt them for their OpenID identifier. You may also pre-set the identifier by passing an identifier parameter to the URL (Example: /auth/open_id?openid_url=yahoo.com). + # + # A list of all OpenID stores is available at http://github.com/openid/ruby-openid/tree/master/lib/openid/store/ + # + # # OmniAuth Builder + # + # If OpenID is one of several authentication strategies, use the OmniAuth Builder: + # + # require 'omniauth/openid' + # require 'omniauth/basic' # for Campfire + # require 'openid/store/filesystem' + # + # use OmniAuth::Builder do + # provider :open_id, OpenID::Store::Filesystem.new('/tmp') + # provider :campfire + # end + # + # # Configured Identifiers + # + # You may pre-configure an OpenID identifier. For example, to use Google's main OpenID endpoint: + # + # use OmniAuth::Builder do + # provider :open_id, nil, :name => 'google', :identifier => 'https://www.google.com/accounts/o8/id' + # end + # + # Note the use of nil, which will trigger ruby-openid's default Memory Store. + module OpenID; end + module Strategies autoload :OpenID, 'omniauth/strategies/open_id' autoload :GoogleApps, 'omniauth/strategies/google_apps' diff --git a/oa-openid/lib/omniauth/openid/gapps.rb b/oa-openid/lib/omniauth/openid/gapps.rb new file mode 100644 index 0000000..e4e0d06 --- /dev/null +++ b/oa-openid/lib/omniauth/openid/gapps.rb @@ -0,0 +1,32 @@ +require 'openid/consumer' +require 'gapps_openid' + +module OpenID + # Because gapps_openid changes the discovery order + # (looking first for Google Apps, then anything else), + # we need to monkeypatch it to make it play nicely + # with others. + def self.discover(uri) + discovered = self.default_discover(uri) + + if discovered.last.empty? + info = discover_google_apps(uri) + return info if info + end + + return discovered + rescue OpenID::DiscoveryFailure => e + info = discover_google_apps(uri) + + if info.nil? + raise e + else + return info + end + end + + def self.discover_google_apps(uri) + discovery = GoogleDiscovery.new + discovery.perform_discovery(uri) + end +end \ No newline at end of file diff --git a/oa-openid/lib/omniauth/strategies/google_apps.rb b/oa-openid/lib/omniauth/strategies/google_apps.rb index fb7be1d..84e16a1 100644 --- a/oa-openid/lib/omniauth/strategies/google_apps.rb +++ b/oa-openid/lib/omniauth/strategies/google_apps.rb @@ -8,6 +8,13 @@ module OmniAuth super(app, store, options) end + def get_identifier + OmniAuth::Form.build('Google Apps Authentication') do + label_field('Google Apps Domain', 'domain') + input_field('url', 'domain') + end.to_response + end + def identifier options[:domain] || request['domain'] end diff --git a/oa-openid/lib/omniauth/strategies/open_id.rb b/oa-openid/lib/omniauth/strategies/open_id.rb index af3677b..07237f5 100644 --- a/oa-openid/lib/omniauth/strategies/open_id.rb +++ b/oa-openid/lib/omniauth/strategies/open_id.rb @@ -1,5 +1,5 @@ require 'rack/openid' -require 'gapps_openid' +require 'omniauth/openid/gapps' require 'omniauth/openid' module OmniAuth @@ -24,7 +24,7 @@ module OmniAuth } def initialize(app, store = nil, options = {}) - super(app, options[:name] || :open_id) + super(app, options.delete(:name) || :open_id) @options = options @options[:required] ||= [AX[:email], AX[:first_name], AX[:last_name], 'email', 'fullname'] @options[:optional] ||= [AX[:nickname], AX[:city], AX[:state], AX[:website], AX[:image], 'postcode', 'nickname'] @@ -47,7 +47,7 @@ module OmniAuth end def identifier - request[IDENTIFIER_URL_PARAMETER] + options[:identifier] || request[IDENTIFIER_URL_PARAMETER] end def request_phase @@ -74,22 +74,20 @@ module OmniAuth def callback_phase env['REQUEST_METHOD'] = 'GET' - openid = Rack::OpenID.new(lambda{|env| [200,{},[]]}, @store) openid.call(env) - resp = env.delete('rack.openid.response') - if resp && resp.status == :success - request['auth'] = auth_hash(resp) - @app.call(env) + @openid_response = env.delete('rack.openid.response') + if @openid_response && @openid_response.status == :success + super else fail!(:invalid_credentials) end end - def auth_hash(response) + def auth_hash OmniAuth::Utils.deep_merge(super(), { - 'uid' => response.display_identifier, - 'user_info' => user_info(response) + 'uid' => @openid_response.display_identifier, + 'user_info' => user_info(@openid_response) }) end diff --git a/omniauth/Gemfile.lock b/omniauth/Gemfile.lock deleted file mode 100644 index 65d5dd9..0000000 --- a/omniauth/Gemfile.lock +++ /dev/null @@ -1,106 +0,0 @@ -PATH - remote: /Users/mbleigh/gems/omniauth/oa-basic - specs: - oa-basic (0.1.1) - multi_json (~> 0.0.2) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - rest-client (~> 1.6.0) - -PATH - remote: /Users/mbleigh/gems/omniauth/oa-core - specs: - oa-core (0.1.1) - rack (~> 1.1) - -PATH - remote: /Users/mbleigh/gems/omniauth/oa-enterprise - specs: - oa-enterprise (0.1.1) - net-ldap (~> 0.1.1) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - rubyntlm (~> 0.1.1) - -PATH - remote: /Users/mbleigh/gems/omniauth/oa-oauth - specs: - oa-oauth (0.1.1) - multi_json (~> 0.0.2) - nokogiri (~> 1.4.2) - oa-core (= 0.1.1) - oauth (~> 0.4.0) - oauth2 (~> 0.0.10) - -PATH - remote: /Users/mbleigh/gems/omniauth/oa-openid - specs: - oa-openid (0.1.1) - oa-core (= 0.1.1) - rack-openid (~> 1.1.1) - ruby-openid-apps-discovery - -PATH - remote: . - specs: - omniauth (0.1.1) - oa-basic (= 0.1.1) - oa-core (= 0.1.1) - oa-enterprise (= 0.1.1) - oa-oauth (= 0.1.1) - oa-openid (= 0.1.1) - -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.0) - crack (0.1.8) - faraday (0.4.6) - addressable (>= 2.1.1) - rack (>= 1.0.1) - json (1.4.3) - mg (0.0.8) - rake - mime-types (1.16) - multi_json (0.0.4) - net-ldap (0.1.1) - nokogiri (1.4.3.1) - oauth (0.4.3) - oauth2 (0.0.13) - faraday (~> 0.4.1) - multi_json (>= 0.0.4) - rack (1.2.1) - rack-openid (1.1.1) - rack (>= 0.4) - ruby-openid (>= 2.0.3) - rack-test (0.5.4) - rack (>= 1.0) - rake (0.8.7) - rest-client (1.6.0) - mime-types (>= 1.16) - rspec (1.3.0) - ruby-openid (2.1.8) - ruby-openid-apps-discovery (1.2.0) - ruby-openid (>= 2.1.7) - rubyntlm (0.1.1) - webmock (1.3.4) - addressable (>= 2.1.1) - crack (>= 0.1.7) - -PLATFORMS - ruby - -DEPENDENCIES - json (~> 1.4.3) - mg (~> 0.0.8) - oa-basic! - oa-core! - oa-enterprise! - oa-oauth! - oa-openid! - omniauth! - rack - rack-test (~> 0.5.4) - rake - rspec (~> 1.3.0) - webmock (~> 1.3.4)