diff --git a/.autotest b/.autotest
deleted file mode 100644
index bf63ba1..0000000
--- a/.autotest
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'autotest/bundler'
-
-Autotest.add_hook :initialize do |at|
- at.clear_mappings
-
- at.add_mapping(%r{^.*/spec/.*_spec.rb$}) do |filename, _|
- filename
- end
-end
diff --git a/.travis.yml b/.travis.yml
index 36b2ab4..f3f6046 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,7 @@
rvm:
- 1.8.7
- 1.9.2
+ - jruby
+ - rbx
+ - rbx-2.0
- ree
diff --git a/Guardfile b/Guardfile
new file mode 100644
index 0000000..1a3d01a
--- /dev/null
+++ b/Guardfile
@@ -0,0 +1,27 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec/" }
+
+ # Rails example
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec/" }
+ watch('spec/spec_helper.rb') { "spec/" }
+ watch('config/routes.rb') { "spec/routing" }
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
+ # Capybara request specs
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
+end
+
+
+guard 'bundler' do
+ watch('Gemfile')
+ # Uncomment next line if Gemfile contain `gemspec' command
+ # watch(/^.+\.gemspec/)
+end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..811fa0e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2011 Michael Bleigh and Intridea, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
index 15123a5..bcb527b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-OmniAuth: Standardized Multi-Provider Authentication
-====================================================
+# OmniAuth: Standardized Multi-Provider Authentication
OmniAuth is a new Rack-based authentication system for multi-provider external
authentcation. OmniAuth is built from the ground up on the philosophy that
**authentication is not the same as identity**, and is based on two
@@ -13,22 +12,20 @@ observations:
to one, three, or twelve other services. Modern authentication systems
should allow a user's identity to be associated with many authentications.
-Installation
-------------
+## Installation
To install OmniAuth, simply install the gem:
gem install omniauth
-Continuous Integration
-----------------------
-[![Build Status](https://secure.travis-ci.org/intridea/omniauth.png)](http://travis-ci.org/intridea/omniauth)
+## Continuous Integration
+[![Build Status](https://travis-ci.org/intridea/omniauth.png)](http://travis-ci.org/intridea/omniauth)
-Providers
----------
+## Providers
OmniAuth currently supports the following external providers:
* via OAuth (OAuth 1.0, OAuth 2, and xAuth)
* 37signals ID (credit: [mbleigh](https://github.com/mbleigh))
+ * AngelList (credit: [joshuaxls](https://github.com/joshuaxls))
* Bit.ly (credit: [philnash](https://github.com/philnash))
* Blogger (credit: [dsueiro-backing](https://github.com/dsueiro-backing))
* Cobot (credit: [kamal](https://github.com/kamal))
@@ -51,6 +48,7 @@ OmniAuth currently supports the following external providers:
* Instapaper (credit: [micpringle](https://github.com/micpringle))
* LastFM (credit: [tictoc](https://github.com/tictoc))
* LinkedIn (credit: [mbleigh](https://github.com/mbleigh))
+ * Mailchimp (via [srbiv](http://github.com/srbiv))
* Mailru (credit: [lexer](https://github.com/lexer))
* Meetup (credit [coderoshi](https://github.com/coderoshi))
* Miso (credit: [rickenharp](https://github.com/rickenharp))
@@ -74,6 +72,7 @@ OmniAuth currently supports the following external providers:
* Tsohu (credit: [quake](https://github.com/quake))
* Tumblr (credit: [jamiew](https://github.com/jamiew))
* Twitter (credit: [mbleigh](https://github.com/mbleigh))
+ * Viadeo (credit: [guillaug](https://github.com/guillaug))
* Vimeo (credit: [jamiew](https://github.com/jamiew))
* Vkontakte (credit: [german](https://github.com/german))
* WePay (credit: [ryanwood](https://github.com/ryanwood))
@@ -88,19 +87,7 @@ OmniAuth currently supports the following external providers:
* OpenID (credit: [mbleigh](https://github.com/mbleigh))
* Yupoo (credit: [chouti](https://github.com/chouti))
-Compatibility
--------------
-OmniAuth is tested against the following Ruby versions:
-
-* 1.8.7
-* 1.9.1
-* 1.9.2
-* JRuby (note, the Evernote strategy is not available for JRuby)
-* Rubinius
-* REE
-
-Usage
------
+## Usage
OmniAuth is a collection of Rack middleware. To use a single strategy, you simply need to add the middleware:
require 'oa-oauth'
@@ -126,8 +113,7 @@ The hash in question will look something like this:
The `user_info` hash will automatically be populated with as much information about the user as OmniAuth was able to pull from the given API or authentication provider.
-Resources
----------
+## Resources
The best place to find more information is the [OmniAuth Wiki](https://github.com/intridea/omniauth/wiki). Some specific information you might be interested in:
* [CI Build Status](http://travis-ci.org/intridea/omniauth)
@@ -136,20 +122,20 @@ The best place to find more information is the [OmniAuth Wiki](https://github.co
* [Report Issues](https://github.com/intridea/omniauth/issues)
* [Mailing List](http://groups.google.com/group/omniauth)
-OmniAuth Core
--------------
+## Core Team
* **Michael Bleigh** ([mbleigh](https://github.com/mbleigh))
* **Erik Michaels-Ober** ([sferik](https://github.com/sferik))
-Supported Rubies
-----------------
+## Supported Rubies
This library aims to support and is [tested
against](http://travis-ci.org/intridea/omniauth) the following Ruby
implementations:
* Ruby 1.8.7
* Ruby 1.9.2
-* Ruby Enterprise Edition 1.8.7
+* [JRuby](http://www.jruby.org/)
+* [Rubinius](http://rubini.us/)
+* [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com/)
If something doesn't work on one of these interpreters, it should be considered
a bug.
@@ -165,6 +151,5 @@ implementation, you will be personally responsible for providing patches in a
timely fashion. If critical issues for a particular implementation exist at the
time of a major release, support for that Ruby version may be dropped.
-License
--------
-OmniAuth is licensed under the MIT License.
+## License
+OmniAuth is released under the MIT License.
diff --git a/Rakefile b/Rakefile
old mode 100644
new mode 100755
diff --git a/lib/oa-core.rb b/lib/oa-core.rb
new file mode 100644
index 0000000..cf3f19d
--- /dev/null
+++ b/lib/oa-core.rb
@@ -0,0 +1 @@
+require 'omniauth/core'
diff --git a/lib/omniauth/builder.rb b/lib/omniauth/builder.rb
new file mode 100644
index 0000000..49dfddc
--- /dev/null
+++ b/lib/omniauth/builder.rb
@@ -0,0 +1,33 @@
+require 'omniauth/core'
+
+module OmniAuth
+ class Builder < ::Rack::Builder
+ def initialize(app, &block)
+ @app = app
+ 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
+ else
+ middleware = OmniAuth::Strategies.const_get("#{OmniAuth::Utils.camelize(klass.to_s)}")
+ end
+
+ use middleware, *args, &block
+ end
+
+ def call(env)
+ @ins << @app unless @ins.include?(@app)
+ to_app.call(env)
+ end
+ end
+end
diff --git a/lib/omniauth/core.rb b/lib/omniauth/core.rb
new file mode 100644
index 0000000..2c9bd24
--- /dev/null
+++ b/lib/omniauth/core.rb
@@ -0,0 +1,140 @@
+require 'rack'
+require 'singleton'
+
+module OmniAuth
+ module Strategies; end
+
+ autoload :Builder, 'omniauth/builder'
+ autoload :Strategy, 'omniauth/strategy'
+ autoload :Test, 'omniauth/test'
+ autoload :Form, 'omniauth/form'
+
+ def self.strategies
+ @@strategies ||= []
+ end
+
+ class Configuration
+ include Singleton
+
+ @@defaults = {
+ :path_prefix => '/auth',
+ :on_failure => Proc.new do |env|
+ message_key = env['omniauth.error.type']
+ new_path = "#{OmniAuth.config.path_prefix}/failure?message=#{message_key}"
+ [302, {'Location' => new_path, 'Content-Type'=> 'text/html'}, []]
+ end,
+ :form_css => Form::DEFAULT_CSS,
+ :test_mode => false,
+ :allowed_request_methods => [:get, :post],
+ :mock_auth => {
+ :default => {
+ 'provider' => 'default',
+ 'uid' => '1234',
+ 'user_info' => {
+ 'name' => 'Bob Example'
+ }
+ }
+ }
+ }
+
+ 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
+ else
+ @on_failure
+ end
+ end
+
+ def add_mock(provider, mock={})
+ # Stringify keys recursively one level.
+ mock.keys.each do |key|
+ mock[key.to_s] = mock.delete(key)
+ end
+ mock.each_pair do |key, val|
+ if val.is_a? Hash
+ val.keys.each do |subkey|
+ val[subkey.to_s] = val.delete(subkey)
+ end
+ end
+ end
+
+ # Merge with the default mock and ensure provider is correct.
+ mock = self.mock_auth[:default].dup.merge(mock)
+ mock["provider"] = provider.to_s
+
+ # Add it to the mocks.
+ self.mock_auth[provider.to_sym] = mock
+ end
+
+ attr_writer :on_failure
+ attr_accessor :path_prefix, :allowed_request_methods, :form_css, :test_mode, :mock_auth, :full_host
+ end
+
+ def self.config
+ Configuration.instance
+ end
+
+ def self.configure
+ yield config
+ end
+
+ def self.mock_auth_for(provider)
+ config.mock_auth[provider.to_sym] || config.mock_auth[:default]
+ end
+
+ module Utils
+ CAMELIZE_SPECIAL = {
+ 'oauth' => 'OAuth',
+ 'oauth2' => 'OAuth2',
+ 'openid' => 'OpenID',
+ 'open_id' => 'OpenID',
+ 'github' => 'GitHub',
+ 'tripit' => 'TripIt',
+ 'soundcloud' => 'SoundCloud',
+ 'smugmug' => 'SmugMug',
+ 'cas' => 'CAS',
+ 'trademe' => 'TradeMe',
+ 'ldap' => 'LDAP',
+ 'google_oauth2' => 'GoogleOAuth2'
+ }
+
+ 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
+ word.first + camelize(word)[1..-1]
+ end
+ end
+ end
+end
diff --git a/lib/omniauth/form.rb b/lib/omniauth/form.rb
new file mode 100644
index 0000000..20eebd0
--- /dev/null
+++ b/lib/omniauth/form.rb
@@ -0,0 +1,186 @@
+require 'omniauth/core'
+
+module OmniAuth
+ class Form
+ DEFAULT_CSS = <<-CSS
+ body {
+ background: #ccc;
+ font-family: "Lucida Grande", "Lucida Sans", Helvetica, Arial, sans-serif;
+ }
+
+ h1 {
+ text-align: center;
+ margin: 30px auto 0px;
+ font-size: 18px;
+ padding: 10px 10px 15px;
+ background: #555;
+ color: white;
+ width: 320px;
+ border: 10px solid #444;
+ border-bottom: 0;
+ -moz-border-radius-topleft: 10px;
+ -moz-border-radius-topright: 10px;
+ -webkit-border-top-left-radius: 10px;
+ -webkit-border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ }
+
+ h1, form {
+ -moz-box-shadow: 2px 2px 7px rgba(0,0,0,0.3);
+ -webkit-box-shadow: 2px 2px 7px rgba(0,0,0,0.3);
+ }
+
+ form {
+ background: white;
+ border: 10px solid #eee;
+ border-top: 0;
+ padding: 20px;
+ margin: 0px auto 40px;
+ width: 300px;
+ -moz-border-radius-bottomleft: 10px;
+ -moz-border-radius-bottomright: 10px;
+ -webkit-border-bottom-left-radius: 10px;
+ -webkit-border-bottom-right-radius: 10px;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ }
+
+ label {
+ display: block;
+ font-weight: bold;
+ margin-bottom: 5px;
+ }
+
+ input {
+ font-size: 18px;
+ padding: 4px 8px;
+ display: block;
+ margin-bottom: 10px;
+ width: 280px;
+ }
+
+ input#identifier, input#openid_url {
+ background: url(http://openid.net/login-bg.gif) no-repeat;
+ background-position: 0 50%;
+ padding-left: 18px;
+ }
+
+ button {
+ font-size: 22px;
+ padding: 4px 8px;
+ display: block;
+ margin: 20px auto 0;
+ }
+
+ fieldset {
+ border: 1px solid #ccc;
+ border-left: 0;
+ border-right: 0;
+ padding: 10px 0;
+ }
+
+ fieldset input {
+ width: 260px;
+ font-size: 16px;
+ }
+ CSS
+
+ attr_accessor :options
+
+ def initialize(options = {})
+ options[:title] ||= "Authentication Info Required"
+ options[:header_info] ||= ""
+ self.options = options
+
+ @html = ""
+ header(options[:title],options[:header_info])
+ end
+
+ def self.build(title=nil,&block)
+ form = OmniAuth::Form.new(title)
+ form.instance_eval(&block)
+ end
+
+ def label_field(text, target)
+ @html << "\n"
+ self
+ end
+
+ def input_field(type, name)
+ @html << "\n"
+ self
+ end
+
+ def text_field(label, name)
+ label_field(label, name)
+ input_field('text', name)
+ self
+ end
+
+ def password_field(label, name)
+ label_field(label, name)
+ input_field('password', name)
+ self
+ end
+
+ def button(text)
+ @html << "\n"
+ end
+
+ def html(html)
+ @html << html
+ end
+
+ def fieldset(legend, options = {}, &block)
+ @html << "\n
"
+ self
+ end
+
+ def header(title,header_info)
+ @html << <<-HTML
+
+
+
+ #{title}
+ #{css}
+ #{header_info}
+
+
+ #{title}
+
+
+
+ HTML
+ @footer = true
+ self
+ end
+
+ def to_html
+ footer
+ @html
+ end
+
+ def to_response
+ footer
+ Rack::Response.new(@html).finish
+ end
+
+ protected
+
+ def css
+ "\n"
+ end
+ end
+end
diff --git a/lib/omniauth/strategy.rb b/lib/omniauth/strategy.rb
new file mode 100644
index 0000000..28d1ef8
--- /dev/null
+++ b/lib/omniauth/strategy.rb
@@ -0,0 +1,230 @@
+require 'omniauth/core'
+
+module OmniAuth
+ class NoSessionError < StandardError; end
+ # The Strategy is the base unit of OmniAuth's ability to
+ # wrangle multiple providers. Each strategy provided by
+ # OmniAuth includes this mixin to gain the default functionality
+ # necessary to be compatible with the OmniAuth library.
+ module Strategy
+ def self.included(base)
+ OmniAuth.strategies << base
+ base.class_eval do
+ attr_reader :app, :name, :env, :options, :response
+ end
+ end
+
+ def initialize(app, name, *args, &block)
+ @app = app
+ @name = name.to_sym
+ @options = args.last.is_a?(Hash) ? args.pop : {}
+
+ yield self if block_given?
+ end
+
+ def inspect
+ "#<#{self.class.to_s}>"
+ end
+
+ def call(env)
+ dup.call!(env)
+ end
+
+ def call!(env)
+ raise OmniAuth::NoSessionError.new("You must provide a session to use OmniAuth.") unless env['rack.session']
+
+ @env = env
+ @env['omniauth.strategy'] = self if on_auth_path?
+
+ return mock_call!(env) if OmniAuth.config.test_mode
+
+ return request_call if on_request_path? && OmniAuth.config.allowed_request_methods.include?(request.request_method.downcase.to_sym)
+ return callback_call if on_callback_path?
+ return other_phase if respond_to?(:other_phase)
+ @app.call(env)
+ end
+
+ # Performs the steps necessary to run the request phase of a strategy.
+ def request_call
+ setup_phase
+ if response = call_through_to_app
+ response
+ else
+ if request.params['origin']
+ @env['rack.session']['omniauth.origin'] = request.params['origin']
+ elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/)
+ @env['rack.session']['omniauth.origin'] = env['HTTP_REFERER']
+ end
+ request_phase
+ end
+ end
+
+ # Performs the steps necessary to run the callback phase of a strategy.
+ def callback_call
+ setup_phase
+ @env['omniauth.origin'] = session.delete('omniauth.origin')
+ @env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
+
+ callback_phase
+ end
+
+ def on_auth_path?
+ on_request_path? || on_callback_path?
+ end
+
+ def on_request_path?
+ current_path.casecmp(request_path) == 0
+ end
+
+ def on_callback_path?
+ current_path.casecmp(callback_path) == 0
+ end
+
+ def mock_call!(env)
+ return mock_request_call if on_request_path?
+ return mock_callback_call if on_callback_path?
+ call_app!
+ end
+
+ def mock_request_call
+ setup_phase
+ return response if response = call_through_to_app
+
+ if request.params['origin']
+ @env['rack.session']['omniauth.origin'] = request.params['origin']
+ elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/)
+ @env['rack.session']['omniauth.origin'] = env['HTTP_REFERER']
+ end
+ redirect(script_name + callback_path + query_string)
+ end
+
+ def mock_callback_call
+ setup_phase
+ mocked_auth = OmniAuth.mock_auth_for(name.to_sym)
+ if mocked_auth.is_a?(Symbol)
+ fail!(mocked_auth)
+ else
+ @env['omniauth.auth'] = mocked_auth
+ @env['omniauth.origin'] = session.delete('omniauth.origin')
+ @env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
+ call_app!
+ end
+ end
+
+ def setup_phase
+ if options[:setup].respond_to?(:call)
+ options[:setup].call(env)
+ elsif options[:setup]
+ setup_env = env.merge('PATH_INFO' => setup_path, 'REQUEST_METHOD' => 'GET')
+ call_app!(setup_env)
+ end
+ end
+
+ def request_phase
+ raise NotImplementedError
+ end
+
+ def callback_phase
+ @env['omniauth.auth'] = auth_hash
+ @env['omniauth.params'] = session['query_params'] || {}
+ session['query_params'] = nil if session['query_params']
+ call_app!
+ end
+
+ def path_prefix
+ options[:path_prefix] || OmniAuth.config.path_prefix
+ end
+
+ def request_path
+ options[:request_path] || "#{path_prefix}/#{name}"
+ end
+
+ def callback_path
+ options[:callback_path] || "#{path_prefix}/#{name}/callback"
+ end
+
+ def setup_path
+ options[:setup_path] || "#{path_prefix}/#{name}/setup"
+ end
+
+ def current_path
+ request.path_info.downcase.sub(/\/$/,'')
+ end
+
+ def query_string
+ request.query_string.empty? ? "" : "?#{request.query_string}"
+ end
+
+ def call_through_to_app
+ status, headers, body = *call_app!
+ session['query_params'] = Rack::Request.new(env).params
+ @response = Rack::Response.new(body, status, headers)
+
+ status == 404 ? nil : @response.finish
+ end
+
+ def call_app!(env = @env)
+ @app.call(env)
+ end
+
+ def auth_hash
+ {
+ 'provider' => name.to_s,
+ 'uid' => nil
+ }
+ end
+
+ def full_host
+ case OmniAuth.config.full_host
+ when String
+ OmniAuth.config.full_host
+ when Proc
+ OmniAuth.config.full_host.call(env)
+ else
+ uri = URI.parse(request.url.gsub(/\?.*$/,''))
+ uri.path = ''
+ uri.query = nil
+ uri.to_s
+ end
+ end
+
+ def callback_url
+ full_host + script_name + callback_path + query_string
+ end
+
+ def script_name
+ @env['SCRIPT_NAME'] || ''
+ end
+
+ def session
+ @env['rack.session']
+ end
+
+ def request
+ @request ||= Rack::Request.new(@env)
+ end
+
+ def redirect(uri)
+ r = Rack::Response.new
+
+ if options[:iframe]
+ r.write("")
+ else
+ r.write("Redirecting to #{uri}...")
+ r.redirect(uri)
+ end
+
+ r.finish
+ end
+
+ def user_info; {} end
+
+ def fail!(message_key, exception = nil)
+ self.env['omniauth.error'] = exception
+ self.env['omniauth.error.type'] = message_key.to_sym
+ self.env['omniauth.error.strategy'] = self
+
+ OmniAuth.config.on_failure.call(self.env)
+ end
+ end
+end
diff --git a/lib/omniauth/test.rb b/lib/omniauth/test.rb
new file mode 100644
index 0000000..bce2561
--- /dev/null
+++ b/lib/omniauth/test.rb
@@ -0,0 +1,12 @@
+module OmniAuth
+
+ # Support for testing OmniAuth strategies.
+ module Test
+
+ autoload :PhonySession, 'omniauth/test/phony_session'
+ autoload :StrategyMacros, 'omniauth/test/strategy_macros'
+ autoload :StrategyTestCase, 'omniauth/test/strategy_test_case'
+
+ end
+
+end
diff --git a/lib/omniauth/test/phony_session.rb b/lib/omniauth/test/phony_session.rb
new file mode 100644
index 0000000..768fa74
--- /dev/null
+++ b/lib/omniauth/test/phony_session.rb
@@ -0,0 +1,8 @@
+class OmniAuth::Test::PhonySession
+ def initialize(app); @app = app end
+ def call(env)
+ @session ||= (env['rack.session'] || {})
+ env['rack.session'] = @session
+ @app.call(env)
+ end
+end
diff --git a/lib/omniauth/test/strategy_macros.rb b/lib/omniauth/test/strategy_macros.rb
new file mode 100644
index 0000000..2d897e0
--- /dev/null
+++ b/lib/omniauth/test/strategy_macros.rb
@@ -0,0 +1,34 @@
+module OmniAuth
+
+ module Test
+
+ module StrategyMacros
+
+ def sets_an_auth_hash
+ it 'should set an auth hash' do
+ 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.env['omniauth.auth'] || {})['provider'].should == provider
+ end
+ end
+
+ def sets_uid_to(uid)
+ it "should set the UID to #{uid}" do
+ (last_request.env['omniauth.auth'] || {})['uid'].should == uid
+ end
+ end
+
+ def sets_user_info_to(user_info)
+ it "should set the user_info to #{user_info}" do
+ (last_request.env['omniauth.auth'] || {})['user_info'].should == user_info
+ end
+ end
+ end
+
+ end
+
+end
diff --git a/lib/omniauth/test/strategy_test_case.rb b/lib/omniauth/test/strategy_test_case.rb
new file mode 100644
index 0000000..9dc67e8
--- /dev/null
+++ b/lib/omniauth/test/strategy_test_case.rb
@@ -0,0 +1,49 @@
+require 'rack'
+require 'omniauth/test'
+
+module OmniAuth
+
+ module Test
+
+ # Support for testing OmniAuth strategies.
+ #
+ # @example Usage
+ # class MyStrategyTest < Test::Unit::TestCase
+ # include OmniAuth::Test::StrategyTestCase
+ # def strategy
+ # # return the parameters to a Rack::Builder map call:
+ # [MyStrategy.new, :some, :configuration, :options => 'here']
+ # end
+ # setup do
+ # post '/auth/my_strategy/callback', :user => { 'name' => 'Dylan', 'id' => '445' }
+ # end
+ # end
+ module StrategyTestCase
+
+ def app
+ strat = self.strategy
+ resp = self.app_response
+ Rack::Builder.new {
+ use OmniAuth::Test::PhonySession
+ use *strat
+ run lambda {|env| [404, {'Content-Type' => 'text/plain'}, [resp || env.key?('omniauth.auth').to_s]] }
+ }.to_app
+ end
+
+ def app_response
+ nil
+ end
+
+ def session
+ last_request.env['rack.session']
+ end
+
+ def strategy
+ raise NotImplementedError.new('Including specs must define #strategy')
+ end
+
+ end
+
+ end
+
+end
diff --git a/oa-core/lib/omniauth/core.rb b/oa-core/lib/omniauth/core.rb
index 8b1e771..5a2201a 100644
--- a/oa-core/lib/omniauth/core.rb
+++ b/oa-core/lib/omniauth/core.rb
@@ -56,10 +56,14 @@ module OmniAuth
def add_mock(provider, mock={})
# Stringify keys recursively one level.
- mock.stringify_keys!
- mock.keys.each do|key|
- if mock[key].is_a? Hash
- mock[key].stringify_keys!
+ mock.keys.each do |key|
+ mock[key.to_s] = mock.delete(key)
+ end
+ mock.each_pair do |key, val|
+ if val.is_a? Hash
+ val.keys.each do |subkey|
+ val[subkey.to_s] = val.delete(subkey)
+ end
end
end
@@ -99,7 +103,8 @@ module OmniAuth
'smugmug' => 'SmugMug',
'cas' => 'CAS',
'trademe' => 'TradeMe',
- 'ldap' => 'LDAP'
+ 'ldap' => 'LDAP',
+ 'google_oauth2' => 'GoogleOAuth2'
}
module_function
diff --git a/oa-core/lib/omniauth/strategy.rb b/oa-core/lib/omniauth/strategy.rb
index 1721474..41576e0 100644
--- a/oa-core/lib/omniauth/strategy.rb
+++ b/oa-core/lib/omniauth/strategy.rb
@@ -110,7 +110,7 @@ module OmniAuth
elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/)
@env['rack.session']['omniauth.origin'] = env['HTTP_REFERER']
end
- redirect(script_name + callback_path)
+ redirect(script_name + callback_path + query_string)
end
def mock_callback_call
diff --git a/oa-core/oa-core.gemspec b/oa-core/oa-core.gemspec
index 8a168f5..32868c9 100644
--- a/oa-core/oa-core.gemspec
+++ b/oa-core/oa-core.gemspec
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
@@ -9,19 +9,18 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'simplecov', '~> 0.4'
gem.add_development_dependency 'rack-test', '~> 0.5'
gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rdiscount', '~> 1.6'
gem.add_development_dependency 'rspec', '~> 2.5'
gem.add_development_dependency 'yard', '~> 0.7'
- gem.add_development_dependency 'ZenTest', '~> 4.5'
- gem.name = 'oa-core'
- gem.version = OmniAuth::Version::STRING
- gem.description = %q{Core strategies for OmniAuth.}
- gem.summary = gem.description
- gem.email = ['michael@intridea.com', 'sferik@gmail.com']
- gem.homepage = 'http://github.com/intridea/omniauth'
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.description = %q{Core strategies for OmniAuth.}
+ gem.email = ['michael@intridea.com', 'sferik@gmail.com']
gem.files = `git ls-files`.split("\n")
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.name = 'oa-core'
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
+ gem.summary = gem.description
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.version = OmniAuth::Version::STRING
end
diff --git a/oa-core/spec/omniauth/strategy_spec.rb b/oa-core/spec/omniauth/strategy_spec.rb
index 1e8763c..7137f6b 100644
--- a/oa-core/spec/omniauth/strategy_spec.rb
+++ b/oa-core/spec/omniauth/strategy_spec.rb
@@ -296,6 +296,11 @@ describe OmniAuth::Strategy do
strategy.call(make_env('/AUTH/TeSt/CaLlBAck')).should == strategy.call(make_env('/auth/test/callback'))
end
+ it 'should maintain query string parameters' do
+ response = strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'cheese=stilton'))
+ response[1]['Location'].should == '/auth/test/callback?cheese=stilton'
+ end
+
it 'should not short circuit requests outside of authentication' do
strategy.call(make_env('/')).should == app.call(make_env('/'))
end
diff --git a/oa-enterprise/README.rdoc b/oa-enterprise/README.rdoc
index 7606c1c..bbd7976 100644
--- a/oa-enterprise/README.rdoc
+++ b/oa-enterprise/README.rdoc
@@ -66,7 +66,40 @@ are not familiar with these authentication methods, please just avoid them.
Direct users to '/auth/ldap' to have them authenticated via your
company's LDAP server.
-
+
+== SAML
+
+Use the SAML strategy as a middleware in your application:
+
+ require 'omniauth/enterprise'
+ use OmniAuth::Strategies::SAML,
+ :assertion_consumer_service_url => "consumer_service_url",
+ :issuer => "issuer",
+ :idp_sso_target_url => "idp_sso_target_url",
+ :idp_cert_fingerprint => "E7:91:B2:E1:...",
+ :name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
+
+:assertion_consumer_service_url
+ The URL at which the SAML assertion should be received.
+
+:issuer
+ The name of your application. Some identity providers might need this to establish the
+ identity of the service provider requesting the login.
+
+:idp_sso_target_url
+ The URL to which the authentication request should be sent. This would be on the identity provider.
+
+:idp_cert_fingerprint
+ The certificate fingerprint, e.g. "90:CC:16:F0:8D:A6:D1:C6:BB:27:2D:BA:93:80:1A:1F:16:8E:4E:08".
+ This is provided from the identity provider when setting up the relationship.
+
+:name_identifier_format
+ Describes the format of the username required by this application.
+ If you need the email address, use "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress".
+ See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf section 8.3 for
+ other options. Note that the identity provider might not support all options.
+
+
== Multiple Strategies
If you're using multiple strategies together, use OmniAuth's Builder. That's
diff --git a/oa-enterprise/lib/omniauth/enterprise.rb b/oa-enterprise/lib/omniauth/enterprise.rb
index 2e66858..77321f2 100644
--- a/oa-enterprise/lib/omniauth/enterprise.rb
+++ b/oa-enterprise/lib/omniauth/enterprise.rb
@@ -4,5 +4,6 @@ module OmniAuth
module Strategies
autoload :CAS, 'omniauth/strategies/cas'
autoload :LDAP, 'omniauth/strategies/ldap'
+ autoload :SAML, 'omniauth/strategies/saml'
end
end
diff --git a/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb b/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb
index f4876c6..3f53345 100644
--- a/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb
+++ b/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb
@@ -125,12 +125,12 @@ module OmniAuth
end
def search(options={}, &block)
- base = options[:base]
+ base = options[:base] || @base
filter = options[:filter]
limit = options[:limit]
args = {
- :base => @base,
+ :base => base,
:filter => filter,
:size => limit
}
diff --git a/oa-enterprise/lib/omniauth/strategies/saml.rb b/oa-enterprise/lib/omniauth/strategies/saml.rb
new file mode 100644
index 0000000..4238b11
--- /dev/null
+++ b/oa-enterprise/lib/omniauth/strategies/saml.rb
@@ -0,0 +1,50 @@
+require 'omniauth/enterprise'
+
+module OmniAuth
+ module Strategies
+ class SAML
+ include OmniAuth::Strategy
+ autoload :AuthRequest, 'omniauth/strategies/saml/auth_request'
+ autoload :AuthResponse, 'omniauth/strategies/saml/auth_response'
+ autoload :ValidationError, 'omniauth/strategies/saml/validation_error'
+ autoload :XMLSecurity, 'omniauth/strategies/saml/xml_security'
+
+ @@settings = {}
+
+ def initialize(app, options={})
+ super(app, :saml)
+ @@settings = {
+ :assertion_consumer_service_url => options[:assertion_consumer_service_url],
+ :issuer => options[:issuer],
+ :idp_sso_target_url => options[:idp_sso_target_url],
+ :idp_cert_fingerprint => options[:idp_cert_fingerprint],
+ :name_identifier_format => options[:name_identifier_format] || "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
+ }
+ end
+
+ def request_phase
+ request = OmniAuth::Strategies::SAML::AuthRequest.new
+ redirect(request.create(@@settings))
+ end
+
+ def callback_phase
+ begin
+ response = OmniAuth::Strategies::SAML::AuthResponse.new(request.params['SAMLResponse'])
+ response.settings = @@settings
+ @name_id = response.name_id
+ return fail!(:invalid_ticket, 'Invalid SAML Ticket') if @name_id.nil? || @name_id.empty?
+ super
+ rescue ArgumentError => e
+ fail!(:invalid_ticket, 'Invalid SAML Response')
+ end
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(super, {
+ 'uid' => @name_id
+ })
+ end
+
+ end
+ end
+end
diff --git a/oa-enterprise/lib/omniauth/strategies/saml/auth_request.rb b/oa-enterprise/lib/omniauth/strategies/saml/auth_request.rb
new file mode 100644
index 0000000..8129f5f
--- /dev/null
+++ b/oa-enterprise/lib/omniauth/strategies/saml/auth_request.rb
@@ -0,0 +1,38 @@
+require "base64"
+require "uuid"
+require "zlib"
+require "cgi"
+
+module OmniAuth
+ module Strategies
+ class SAML
+ class AuthRequest
+
+ def create(settings, params = {})
+ uuid = "_" + UUID.new.generate
+ time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
+
+ request =
+ "" +
+ "#{settings[:issuer]}\n" +
+ "\n" +
+ "" +
+ "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\n" +
+ ""
+
+ deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
+ base64_request = Base64.encode64(deflated_request)
+ encoded_request = CGI.escape(base64_request)
+ request_params = "?SAMLRequest=" + encoded_request
+
+ params.each_pair do |key, value|
+ request_params << "{key}=#{CGI.escape(value.to_s)}"
+ end
+
+ settings[:idp_sso_target_url] + request_params
+ end
+
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/oa-enterprise/lib/omniauth/strategies/saml/auth_response.rb b/oa-enterprise/lib/omniauth/strategies/saml/auth_response.rb
new file mode 100644
index 0000000..d63f39d
--- /dev/null
+++ b/oa-enterprise/lib/omniauth/strategies/saml/auth_response.rb
@@ -0,0 +1,141 @@
+require "time"
+
+module OmniAuth
+ module Strategies
+ class SAML
+ class AuthResponse
+
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
+
+ attr_accessor :options, :response, :document, :settings
+
+ def initialize(response, options = {})
+ raise ArgumentError.new("Response cannot be nil") if response.nil?
+ self.options = options
+ self.response = response
+ self.document = OmniAuth::Strategies::SAML::XMLSecurity::SignedDocument.new(Base64.decode64(response))
+ end
+
+ def is_valid?
+ validate(soft = true)
+ end
+
+ def validate!
+ validate(soft = false)
+ end
+
+ # The value of the user identifier as designated by the initialization request response
+ def name_id
+ @name_id ||= begin
+ node = REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Subject/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
+ node ||= REXML::XPath.first(document, "/p:Response[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Assertion/a:Subject/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
+ node.nil? ? nil : node.text
+ end
+ end
+
+ # A hash of alle the attributes with the response. Assuming there is only one value for each key
+ def attributes
+ @attr_statements ||= begin
+ result = {}
+
+ stmt_element = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AttributeStatement", { "p" => PROTOCOL, "a" => ASSERTION })
+ return {} if stmt_element.nil?
+
+ stmt_element.elements.each do |attr_element|
+ name = attr_element.attributes["Name"]
+ value = attr_element.elements.first.text
+
+ result[name] = value
+ end
+
+ result.keys.each do |key|
+ result[key.intern] = result[key]
+ end
+
+ result
+ end
+ end
+
+ # When this user session should expire at latest
+ def session_expires_at
+ @expires_at ||= begin
+ node = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AuthnStatement", { "p" => PROTOCOL, "a" => ASSERTION })
+ parse_time(node, "SessionNotOnOrAfter")
+ end
+ end
+
+ # Conditions (if any) for the assertion to run
+ def conditions
+ @conditions ||= begin
+ REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Conditions", { "p" => PROTOCOL, "a" => ASSERTION })
+ end
+ end
+
+ private
+
+ def validation_error(message)
+ raise OmniAuth::Strategies::SAML::ValidationError.new(message)
+ end
+
+ def validate(soft = true)
+ validate_response_state(soft) &&
+ validate_conditions(soft) &&
+ document.validate(get_fingerprint, soft)
+ end
+
+ def validate_response_state(soft = true)
+ if response.empty?
+ return soft ? false : validation_error("Blank response")
+ end
+
+ if settings.nil?
+ return soft ? false : validation_error("No settings on response")
+ end
+
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
+ return soft ? false : validation_error("No fingerprint or certificate on settings")
+ end
+
+ true
+ end
+
+ def get_fingerprint
+ if settings.idp_cert
+ cert = OpenSSL::X509::Certificate.new(settings.idp_cert)
+ Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
+ else
+ settings.idp_cert_fingerprint
+ end
+ end
+
+ def validate_conditions(soft = true)
+ return true if conditions.nil?
+ return true if options[:skip_conditions]
+
+ if not_before = parse_time(conditions, "NotBefore")
+ if Time.now.utc < not_before
+ return soft ? false : validation_error("Current time is earlier than NotBefore condition")
+ end
+ end
+
+ if not_on_or_after = parse_time(conditions, "NotOnOrAfter")
+ if Time.now.utc >= not_on_or_after
+ return soft ? false : validation_error("Current time is on or after NotOnOrAfter condition")
+ end
+ end
+
+ true
+ end
+
+ def parse_time(node, attribute)
+ if node && node.attributes[attribute]
+ Time.parse(node.attributes[attribute])
+ end
+ end
+
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/oa-enterprise/lib/omniauth/strategies/saml/validation_error.rb b/oa-enterprise/lib/omniauth/strategies/saml/validation_error.rb
new file mode 100644
index 0000000..f62f763
--- /dev/null
+++ b/oa-enterprise/lib/omniauth/strategies/saml/validation_error.rb
@@ -0,0 +1,8 @@
+module OmniAuth
+ module Strategies
+ class SAML
+ class ValidationError < Exception
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/oa-enterprise/lib/omniauth/strategies/saml/xml_security.rb b/oa-enterprise/lib/omniauth/strategies/saml/xml_security.rb
new file mode 100644
index 0000000..06974c8
--- /dev/null
+++ b/oa-enterprise/lib/omniauth/strategies/saml/xml_security.rb
@@ -0,0 +1,126 @@
+# The contents of this file are subject to the terms
+# of the Common Development and Distribution License
+# (the License). You may not use this file except in
+# compliance with the License.
+#
+# You can obtain a copy of the License at
+# https://opensso.dev.java.net/public/CDDLv1.0.html or
+# opensso/legal/CDDLv1.0.txt
+# See the License for the specific language governing
+# permission and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL
+# Header Notice in each file and include the License file
+# at opensso/legal/CDDLv1.0.txt.
+# If applicable, add the following below the CDDL Header,
+# with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
+#
+# Copyright 2007 Sun Microsystems Inc. All Rights Reserved
+# Portions Copyrighted 2007 Todd W Saxton.
+
+require 'rubygems'
+require "rexml/document"
+require "rexml/xpath"
+require "openssl"
+require "xmlcanonicalizer"
+require "digest/sha1"
+
+module OmniAuth
+ module Strategies
+ class SAML
+
+ module XMLSecurity
+
+ class SignedDocument < REXML::Document
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
+
+ attr_accessor :signed_element_id
+
+ def initialize(response)
+ super(response)
+ extract_signed_element_id
+ end
+
+ def validate(idp_cert_fingerprint, soft = true)
+ # get cert from response
+ base64_cert = self.elements["//ds:X509Certificate"].text
+ cert_text = Base64.decode64(base64_cert)
+ cert = OpenSSL::X509::Certificate.new(cert_text)
+
+ # check cert matches registered idp cert
+ fingerprint = Digest::SHA1.hexdigest(cert.to_der)
+
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
+ return soft ? false : (raise OmniAuth::Strategies::SAML::ValidationError.new("Fingerprint mismatch"))
+ end
+
+ validate_doc(base64_cert, soft)
+ end
+
+ def validate_doc(base64_cert, soft = true)
+ # validate references
+
+ # check for inclusive namespaces
+
+ inclusive_namespaces = []
+ inclusive_namespace_element = REXML::XPath.first(self, "//ec:InclusiveNamespaces")
+
+ if inclusive_namespace_element
+ prefix_list = inclusive_namespace_element.attributes.get_attribute('PrefixList').value
+ inclusive_namespaces = prefix_list.split(" ")
+ end
+
+ # remove signature node
+ sig_element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
+ sig_element.remove
+
+ # check digests
+ REXML::XPath.each(sig_element, "//ds:Reference", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}) do |ref|
+ uri = ref.attributes.get_attribute("URI").value
+ hashed_element = REXML::XPath.first(self, "//[@ID='#{uri[1,uri.size]}']")
+ canoner = XML::Util::XmlCanonicalizer.new(false, true)
+ canoner.inclusive_namespaces = inclusive_namespaces if canoner.respond_to?(:inclusive_namespaces) && !inclusive_namespaces.empty?
+ canon_hashed_element = canoner.canonicalize(hashed_element)
+ hash = Base64.encode64(Digest::SHA1.digest(canon_hashed_element)).chomp
+ digest_value = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
+
+ if hash != digest_value
+ return soft ? false : (raise OmniAuth::Strategies::SAML::ValidationError.new("Digest mismatch"))
+ end
+ end
+
+ # verify signature
+ canoner = XML::Util::XmlCanonicalizer.new(false, true)
+ signed_info_element = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
+ canon_string = canoner.canonicalize(signed_info_element)
+
+ base64_signature = REXML::XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
+ signature = Base64.decode64(base64_signature)
+
+ # get certificate object
+ cert_text = Base64.decode64(base64_cert)
+ cert = OpenSSL::X509::Certificate.new(cert_text)
+
+ if !cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, canon_string)
+ return soft ? false : (raise OmniAuth::Strategies::SAML::ValidationError.new("Key validation error"))
+ end
+
+ return true
+ end
+
+ private
+
+ def extract_signed_element_id
+ reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
+ self.signed_element_id = reference_element.attribute("URI").value unless reference_element.nil?
+ end
+ end
+ end
+
+ end
+ end
+end
\ No newline at end of file
diff --git a/oa-enterprise/oa-enterprise.gemspec b/oa-enterprise/oa-enterprise.gemspec
index d6b61e9..0eb4c47 100644
--- a/oa-enterprise/oa-enterprise.gemspec
+++ b/oa-enterprise/oa-enterprise.gemspec
@@ -1,31 +1,31 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
- gem.add_runtime_dependency 'addressable', '~> 2.2.6'
- gem.add_runtime_dependency 'nokogiri', '~> 1.5.0'
- gem.add_runtime_dependency 'net-ldap', '~> 0.2.2'
- gem.add_runtime_dependency 'oa-core', OmniAuth::Version::STRING
- gem.add_runtime_dependency 'pyu-ruby-sasl', '~> 0.0.3.1'
- gem.add_runtime_dependency 'rubyntlm', '~> 0.1.1'
- gem.add_development_dependency 'maruku', '~> 0.6'
- gem.add_development_dependency 'simplecov', '~> 0.4'
+ gem.add_dependency 'addressable', '~> 2.2.6'
+ gem.add_dependency 'net-ldap', '~> 0.2.2'
+ gem.add_dependency 'nokogiri', '~> 1.5.0'
+ gem.add_dependency 'oa-core', OmniAuth::Version::STRING
+ gem.add_dependency 'pyu-ruby-sasl', '~> 0.0.3.1'
+ gem.add_dependency 'rubyntlm', '~> 0.1.1'
+ gem.add_dependency 'uuid'
+ gem.add_dependency 'XMLCanonicalizer', '~> 1.0.1'
gem.add_development_dependency 'rack-test', '~> 0.5'
gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rdiscount', '~> 1.6'
gem.add_development_dependency 'rspec', '~> 2.5'
- gem.add_development_dependency 'webmock', '~> 1.6'
+ gem.add_development_dependency 'simplecov', '~> 0.4'
+ gem.add_development_dependency 'webmock', '~> 1.7'
gem.add_development_dependency 'yard', '~> 0.7'
- gem.add_development_dependency 'ZenTest', '~> 4.5'
- gem.name = 'oa-enterprise'
- gem.version = OmniAuth::Version::STRING
+ gem.authors = ['James A. Rosen', 'Ping Yu', 'Michael Bleigh', 'Erik Michaels-Ober', 'Raecoo Cao']
gem.description = %q{Enterprise strategies for OmniAuth.}
- gem.summary = gem.description
- gem.email = ['james.a.rosen@gmail.com', 'ping@intridea.com', 'michael@intridea.com', 'sferik@gmail.com']
- gem.homepage = 'http://github.com/intridea/omniauth'
- gem.authors = ['James A. Rosen', 'Ping Yu', 'Michael Bleigh', 'Erik Michaels-Ober']
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.email = ['james.a.rosen@gmail.com', 'ping@intridea.com', 'michael@intridea.com', 'sferik@gmail.com', 'raecoo@intridea.com']
gem.files = `git ls-files`.split("\n")
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.name = 'oa-enterprise'
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
+ gem.summary = gem.description
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.version = OmniAuth::Version::STRING
end
diff --git a/oa-enterprise/spec/omniauth/strategies/saml_spec.rb b/oa-enterprise/spec/omniauth/strategies/saml_spec.rb
new file mode 100644
index 0000000..2bd1776
--- /dev/null
+++ b/oa-enterprise/spec/omniauth/strategies/saml_spec.rb
@@ -0,0 +1,37 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe OmniAuth::Strategies::SAML, :type => :strategy do
+
+ include OmniAuth::Test::StrategyTestCase
+
+ def strategy
+ [OmniAuth::Strategies::SAML, {
+ :assertion_consumer_service_url => "http://consumer.service.url/auth/saml/callback",
+ :issuer => "https://saml.issuer.url/issuers/29490",
+ :idp_sso_target_url => "https://idp.sso.target_url/signon/29490",
+ :idp_cert_fingerprint => "E7:91:B2:E1:4C:65:2C:49:F3:33:74:0A:58:5A:7E:55:F7:15:7A:33",
+ :name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
+ }]
+ end
+
+ describe 'GET /auth/saml' do
+ before do
+ get '/auth/saml'
+ end
+
+ it 'should get authentication page' do
+ last_response.should be_redirect
+ end
+ end
+
+ describe 'POST /auth/saml/callback' do
+
+ it 'should raise ArgumentError exception without the SAMLResponse parameter' do
+ post '/auth/saml/callback'
+ last_response.should be_redirect
+ last_response.location.should == '/auth/failure?message=invalid_ticket'
+ end
+
+ end
+
+end
\ No newline at end of file
diff --git a/oa-identity/oa-identity.gemspec b/oa-identity/oa-identity.gemspec
index fefdd5c..d6e9e93 100644
--- a/oa-identity/oa-identity.gemspec
+++ b/oa-identity/oa-identity.gemspec
@@ -26,4 +26,3 @@ Gem::Specification.new do |gem|
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
end
-
diff --git a/oa-more/lib/omniauth/more.rb b/oa-more/lib/omniauth/more.rb
index 2e29b23..7b69889 100644
--- a/oa-more/lib/omniauth/more.rb
+++ b/oa-more/lib/omniauth/more.rb
@@ -2,12 +2,11 @@ require 'omniauth/core'
module OmniAuth
module Strategies
- autoload :WindowsLive, 'omniauth/strategies/windows_live'
+ autoload :Draugiem, 'omniauth/strategies/draugiem'
autoload :Flickr, 'omniauth/strategies/flickr'
- autoload :LastFm, 'omniauth/strategies/last_fm'
- autoload :Yupoo, 'omniauth/strategies/yupoo'
- autoload :Ign, 'omniauth/strategies/ign'
- autoload :Draugiem, 'omniauth/strategies/draugiem'
autoload :HttpBasic, 'omniauth/strategies/http_basic'
+ autoload :Ign, 'omniauth/strategies/ign'
+ autoload :LastFm, 'omniauth/strategies/last_fm'
+ autoload :WindowsLive, 'omniauth/strategies/windows_live'
end
end
diff --git a/oa-more/lib/omniauth/strategies/flickr.rb b/oa-more/lib/omniauth/strategies/flickr.rb
deleted file mode 100644
index 84c9b09..0000000
--- a/oa-more/lib/omniauth/strategies/flickr.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require 'omniauth/core'
-require 'digest/md5'
-require 'rest-client'
-require 'multi_json'
-
-module OmniAuth
- module Strategies
- #
- # Authenticate to Flickr
- #
- # @example Basic Usage
- #
- # use OmniAuth::Strategies::Flickr, 'API Key', 'Secret Key', :scope => 'read'
- class Flickr
- include OmniAuth::Strategy
- attr_accessor :api_key, :secret_key, :options
-
- # error catching, based on OAuth2 callback
- class CallbackError < StandardError
- attr_accessor :error, :error_reason
- def initialize(error, error_reason)
- self.error = error
- self.error_reason = error_reason
- end
- end
-
- # @param [Rack Application] app standard middleware application parameter
- # @param [String] api_key the application id as [registered on Flickr](http://www.flickr.com/services/apps/)
- # @param [String] secret_key the application secret as [registered on Flickr](http://www.flickr.com/services/apps/)
- # @option options ['read','write','delete] :scope ('read') the scope of your authorization request; must be `read` or 'write' or 'delete'
- def initialize(app, api_key, secret_key, options = {})
- super(app, :flickr)
- @api_key = api_key
- @secret_key = secret_key
- @options = {:scope => 'read'}.merge(options)
- end
-
- protected
-
- def request_phase
- params = { :api_key => api_key, :perms => options[:scope] }
- params[:api_sig] = flickr_sign(params)
- query_string = params.collect{ |key,value| "#{key}=#{Rack::Utils.escape(value)}" }.join('&')
- redirect "http://flickr.com/services/auth/?#{query_string}"
- end
-
- def callback_phase
- params = { :api_key => api_key, :method => 'flickr.auth.getToken', :frob => request.params['frob'], :format => 'json', :nojsoncallback => '1' }
- params[:api_sig] = flickr_sign(params)
-
- response = RestClient.get('http://api.flickr.com/services/rest/', { :params => params })
- auth = MultiJson.decode(response.to_s)
- raise CallbackError.new(auth['code'],auth['message']) if auth['stat'] == 'fail'
-
- @user = auth['auth']['user']
- @access_token = auth['auth']['token']['_content']
-
- super
- rescue CallbackError => e
- fail!(:invalid_response, e)
- end
-
- def auth_hash
- OmniAuth::Utils.deep_merge(super, {
- 'uid' => @user['nsid'],
- 'credentials' => { 'token' => @access_token },
- 'user_info' => user_info,
- 'extra' => { 'user_hash' => @user }
- })
- end
-
- def user_info
- name = @user['fullname']
- name = @user['username'] if name.nil? || name.empty?
- {
- 'nickname' => @user['username'],
- 'name' => name,
- }
- end
-
- def flickr_sign(params)
- Digest::MD5.hexdigest(secret_key + params.sort{|a,b| a[0].to_s <=> b[0].to_s }.flatten.join)
- end
- end
- end
-end
diff --git a/oa-more/oa-more.gemspec b/oa-more/oa-more.gemspec
index eb355e0..6053657 100644
--- a/oa-more/oa-more.gemspec
+++ b/oa-more/oa-more.gemspec
@@ -1,29 +1,27 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
- gem.add_runtime_dependency 'multi_json', '~> 1.0.0'
- gem.add_runtime_dependency 'oa-core', OmniAuth::Version::STRING
- gem.add_runtime_dependency 'rest-client', '~> 1.6.0'
+ gem.add_dependency 'multi_json', '~> 1.0.0'
+ gem.add_dependency 'oa-core', OmniAuth::Version::STRING
+ gem.add_dependency 'rest-client', '~> 1.6.0'
gem.add_development_dependency 'json_pure', '~> 1.5'
- gem.add_development_dependency 'maruku', '~> 0.6'
- gem.add_development_dependency 'rake', '~> 0.8'
gem.add_development_dependency 'rack-test', '~> 0.5'
+ gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rdiscount', '~> 1.6'
gem.add_development_dependency 'rspec', '~> 2.5'
gem.add_development_dependency 'simplecov', '~> 0.4'
- gem.add_development_dependency 'webmock', '~> 1.6'
+ gem.add_development_dependency 'webmock', '~> 1.7'
gem.add_development_dependency 'yard', '~> 0.7'
- gem.add_development_dependency 'ZenTest', '~> 4.5'
- gem.name = 'oa-more'
- gem.version = OmniAuth::Version::STRING
- gem.description = %q{Additional strategies for OmniAuth.}
- gem.summary = gem.description
- gem.email = 'michael@intridea.com'
- gem.homepage = 'http://github.com/intridea/omniauth'
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.description = %q{Additional strategies for OmniAuth.}
+ gem.email = 'michael@intridea.com'
gem.files = `git ls-files`.split("\n")
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.name = 'oa-more'
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
+ gem.summary = gem.description
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.version = OmniAuth::Version::STRING
end
diff --git a/oa-more/spec/omniauth/strategies/flickr_spec.rb b/oa-more/spec/omniauth/strategies/flickr_spec.rb
deleted file mode 100644
index 3282e36..0000000
--- a/oa-more/spec/omniauth/strategies/flickr_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-
-describe 'OmniAuth::Strategies::Flickr' do
- it 'should initialize with a consumer key and secret' do
- lambda{OmniAuth::Strategies::Flickr.new({},'abc','def')}.should_not raise_error
- end
-end
diff --git a/oa-oauth/lib/omniauth/oauth.rb b/oa-oauth/lib/omniauth/oauth.rb
index 270073c..10bdad7 100644
--- a/oa-oauth/lib/omniauth/oauth.rb
+++ b/oa-oauth/lib/omniauth/oauth.rb
@@ -9,6 +9,7 @@ module OmniAuth
autoload :Dropbox, 'omniauth/strategies/oauth/dropbox'
autoload :Evernote, 'omniauth/strategies/oauth/evernote'
autoload :Flattr, 'omniauth/strategies/oauth/flattr'
+ autoload :Flickr, 'omniauth/strategies/oauth/flickr'
autoload :Goodreads, 'omniauth/strategies/oauth/goodreads'
autoload :Google, 'omniauth/strategies/oauth/google'
autoload :GoogleHealth, 'omniauth/strategies/oauth/google_health'
@@ -24,7 +25,6 @@ module OmniAuth
autoload :Plurk, 'omniauth/strategies/oauth/plurk'
autoload :Rdio, 'omniauth/strategies/oauth/rdio'
autoload :SmugMug, 'omniauth/strategies/oauth/smug_mug'
- autoload :SoundCloud, 'omniauth/strategies/oauth/sound_cloud'
autoload :T163, 'omniauth/strategies/oauth/t163'
autoload :Tqq, 'omniauth/strategies/oauth/tqq'
autoload :TradeMe, 'omniauth/strategies/oauth/trade_me'
@@ -40,6 +40,7 @@ module OmniAuth
autoload :YouTube, 'omniauth/strategies/oauth/you_tube'
autoload :OAuth2, 'omniauth/strategies/oauth2'
+ autoload :AngelList, 'omniauth/strategies/oauth2/angellist'
autoload :Bitly, 'omniauth/strategies/oauth2/bitly'
autoload :Cobot, 'omniauth/strategies/oauth2/cobot'
autoload :Dailymile, 'omniauth/strategies/oauth2/dailymile'
@@ -48,19 +49,26 @@ module OmniAuth
autoload :Foursquare, 'omniauth/strategies/oauth2/foursquare'
autoload :GitHub, 'omniauth/strategies/oauth2/github'
autoload :Glitch, 'omniauth/strategies/oauth2/glitch'
+ autoload :GoogleOAuth2, 'omniauth/strategies/google_oauth2'
autoload :Gowalla, 'omniauth/strategies/oauth2/gowalla'
autoload :Instagram, 'omniauth/strategies/oauth2/instagram'
+ autoload :Liveid, 'omniauth/strategies/oauth2/liveid'
+ autoload :Mailchimp, 'omniauth/strategies/oauth2/mailchimp'
autoload :Mailru, 'omniauth/strategies/oauth2/mailru'
autoload :Mixi, 'omniauth/strategies/oauth2/mixi'
autoload :Renren, 'omniauth/strategies/oauth2/renren'
autoload :Salesforce, 'omniauth/strategies/oauth2/salesforce'
- autoload :TaoBao, 'omniauth/strategies/oauth2/taobao'
+ autoload :SoundCloud, 'omniauth/strategies/oauth2/sound_cloud'
+ autoload :Taobao, 'omniauth/strategies/oauth2/taobao'
autoload :Teambox, 'omniauth/strategies/oauth2/teambox'
autoload :ThirtySevenSignals, 'omniauth/strategies/oauth2/thirty_seven_signals'
+ autoload :Viadeo, 'omniauth/strategies/oauth2/viadeo'
autoload :Vkontakte, 'omniauth/strategies/oauth2/vkontakte'
autoload :WePay, 'omniauth/strategies/oauth2/we_pay'
+
autoload :XAuth, 'omniauth/strategies/xauth'
autoload :Instapaper, 'omniauth/strategies/xauth/instapaper'
+
end
end
diff --git a/oa-oauth/lib/omniauth/strategies/google_oauth2.rb b/oa-oauth/lib/omniauth/strategies/google_oauth2.rb
new file mode 100644
index 0000000..7330315
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/google_oauth2.rb
@@ -0,0 +1,59 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+ # OAuth 2.0 based authentication with Google.
+ class GoogleOAuth2 < OAuth2
+ # @param [Rack Application] app standard middleware application argument
+ # @param [String] client_id the application ID for your client
+ # @param [String] client_secret the application secret
+ # @option options [String] :scope ('https://www.googleapis.com/auth/userinfo.email') space-separated services that you need.
+ def initialize(app, client_id = nil, client_secret = nil, options = {}, &block)
+ client_options = {
+ :site => 'https://accounts.google.com',
+ :authorize_url => '/o/oauth2/auth',
+ :token_url => '/o/oauth2/token'
+ }
+
+ super(app, :google_oauth2, client_id, client_secret, client_options, options, &block)
+ end
+
+ def request_phase
+ google_email_scope = "www.googleapis.com/auth/userinfo.email"
+ options[:scope] ||= "https://#{google_email_scope}"
+ options[:scope] << "https://#{google_email_scope}" unless options[:scope] =~ %r[http[s]?:\/\/#{google_email_scope}]
+ redirect client.auth_code.authorize_url(
+ {:redirect_uri => callback_url, :response_type => "code"}.merge(options))
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(super, {
+ 'uid' => user_info['uid'],
+ 'user_info' => user_info,
+ 'credentials' => {'expires_at' => @access_token.expires_at},
+ 'extra' => {'user_hash' => user_data}
+ })
+ end
+
+ def user_info
+ if user_data['data']['isVerified']
+ email = user_data['data']['email']
+ else
+ email = nil
+ end
+ {
+ 'email' => email,
+ 'uid' => email,
+ 'name' => email
+ }
+ end
+
+ def user_data
+ @data ||=
+ @access_token.get("https://www.googleapis.com/userinfo/email?alt=json").parsed
+ end
+
+ end
+ end
+end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth.rb b/oa-oauth/lib/omniauth/strategies/oauth.rb
index f70c90e..75ac0ab 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth.rb
@@ -40,7 +40,7 @@ module OmniAuth
rescue ::Timeout::Error => e
fail!(:timeout, e)
- rescue ::Net::HTTPFatalError => e
+ rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
end
@@ -60,7 +60,7 @@ module OmniAuth
super
rescue ::Timeout::Error => e
fail!(:timeout, e)
- rescue ::Net::HTTPFatalError => e
+ rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
rescue ::OAuth::Unauthorized => e
fail!(:invalid_credentials, e)
diff --git a/oa-oauth/lib/omniauth/strategies/oauth/dropbox.rb b/oa-oauth/lib/omniauth/strategies/oauth/dropbox.rb
index ecc9773..58b6053 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth/dropbox.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth/dropbox.rb
@@ -31,6 +31,8 @@ module OmniAuth
def user_info
{
'name' => user_data['display_name'],
+ 'uid' => user_data['uid'],
+ 'email' => user_data['email']
}
end
end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth/flickr.rb b/oa-oauth/lib/omniauth/strategies/oauth/flickr.rb
new file mode 100644
index 0000000..e17774a
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/oauth/flickr.rb
@@ -0,0 +1,39 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+
+ class Flickr < OmniAuth::Strategies::OAuth
+ def initialize(app, consumer_key=nil, consumer_secret=nil, options={}, &block)
+ scope = options.delete(:scope) || 'read'
+ options[:authorize_params] ||= {}
+ options[:authorize_params][:perms] = scope
+
+ client_options = {
+ :access_token_path => "/services/oauth/access_token",
+ :authorize_path => "/services/oauth/authorize",
+ :request_token_path => "/services/oauth/request_token",
+ :site => "http://www.flickr.com"
+ }
+ super(app, :flickr, consumer_key, consumer_secret, client_options, options, &block)
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(
+ super, {
+ 'uid' => @access_token.params["user_nsid"],
+ 'user_info' => user_info
+ }
+ )
+ end
+
+ def user_info
+ {
+ 'username' => @access_token.params["username"],
+ 'full_name' => @access_token.params["fullname"]
+ }
+ end
+ end
+ end
+end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth/t163.rb b/oa-oauth/lib/omniauth/strategies/oauth/t163.rb
index 81ebb01..d349cec 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth/t163.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth/t163.rb
@@ -42,6 +42,7 @@ module OmniAuth
'location' => user_hash['location'],
'image' => user_hash['profile_image_url'],
'description' => user_hash['description'],
+ 'email' => user_hash['email'],
'urls' => {
'T163' => 'http://t.163.com',
},
diff --git a/oa-oauth/lib/omniauth/strategies/oauth/tsina.rb b/oa-oauth/lib/omniauth/strategies/oauth/tsina.rb
index 919b5cc..62511a1 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth/tsina.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth/tsina.rb
@@ -68,8 +68,8 @@ module OmniAuth
# MonkeyPath to symbolize tina parameters
def callback_phase
- session[:oauth].symbolize_keys!
- session[:oauth][name.to_sym].symbolize_keys! if session[:oauth][name.to_sym]
+ session[:oauth].stringify_keys!
+ session[:oauth][name.to_s].stringify_keys! if session[:oauth][name.to_s]
super
end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/angellist.rb b/oa-oauth/lib/omniauth/strategies/oauth2/angellist.rb
new file mode 100644
index 0000000..8b47285
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/angellist.rb
@@ -0,0 +1,57 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+ # Authenticate to AngelList utilizing OAuth 2.0 and retrieve
+ # basic user information.
+ #
+ # @example Basic Usage
+ # use OmniAuth::Strategies::AngelList, 'API Key', 'Secret Key'
+ class AngelList < OmniAuth::Strategies::OAuth2
+ # @param [Rack Application] app standard middleware application parameter
+ # @param [String] client_id the application id as [registered on AngelList](http://angel.co/api/oauth/faq)
+ # @param [String] client_secret the application secret as [registered on AngelList](http://bit.ly/api/oauth/faq )
+ def initialize(app, client_id=nil, client_secret=nil, options={}, &block)
+ client_options = {
+ :site => 'https://api.angel.co/',
+ :authorize_url => 'https://angel.co/api/oauth/authorize',
+ :token_url => 'https://angel.co/api/oauth/token'
+ }
+
+ super(app, :angellist, client_id, client_secret, client_options, options, &block)
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(
+ super, {
+ 'uid' => user_data['id'],
+ 'user_info' => user_info,
+ 'extra' => {
+ 'user_hash' => user_data,
+ }
+ }
+ )
+ end
+
+ def user_info
+ {
+ 'name' => user_data['name'],
+ 'bio' => user_data['bio'],
+ 'image' => user_data['image'],
+ 'urls' => {
+ 'AngelList' => user_data['angellist_url'],
+ 'Website' => user_data['online_bio_url']
+ },
+ }
+ end
+
+ def user_data
+ @data ||= begin
+ @access_token.options[:mode] = :query
+ @access_token.get('/1/me').parsed
+ end
+ end
+ end
+ end
+end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/foursquare.rb b/oa-oauth/lib/omniauth/strategies/oauth2/foursquare.rb
index e37b33f..3628244 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth2/foursquare.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/foursquare.rb
@@ -34,7 +34,7 @@ module OmniAuth
end
def user_data
- @data ||= MultiJson.decode(@access_token.get('https://foursquare.com/users/self', {'oauth_token' => @access_token.token}))
+ @data ||= MultiJson.decode(@access_token.get('https://api.foursquare.com/v2/users/self', { :params => { 'oauth_token' => @access_token.token } }).body)
end
def request_phase
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/instagram.rb b/oa-oauth/lib/omniauth/strategies/oauth2/instagram.rb
index e9cce7d..67c027d 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth2/instagram.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/instagram.rb
@@ -42,6 +42,7 @@ module OmniAuth
end
def user_data
+ @access_token.options.merge!({:param_name => 'access_token', :mode => :query})
@data ||= MultiJson.decode(@access_token.get('/v1/users/self'))
end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/liveid.rb b/oa-oauth/lib/omniauth/strategies/oauth2/liveid.rb
new file mode 100644
index 0000000..2f9f7c2
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/liveid.rb
@@ -0,0 +1,67 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+ # Authenticate to Windows Connect utilizing OAuth 2.0 and retrieve
+ # basic user information.
+ #
+ # OAuth 2.0 - MS Documentation
+ # http://msdn.microsoft.com/en-us/library/hh243647.aspx
+ #
+ # Sign-up for account:
+ # http://go.microsoft.com/fwlink/?LinkId=213332
+ #
+ # @example Basic Usage of Liveid
+ #
+ # use OmniAuth::Strategies::Liveid, 'client_id', 'client_secret'
+ class Liveid < OmniAuth::Strategies::OAuth2
+ # @option options [String] :scope separate the scopes by a space
+ def initialize(app, client_id=nil, client_secret=nil, options={}, &block)
+ client_options = {
+ :authorize_url => 'https://oauth.live.com/authorize',
+ :token_url => 'https://oauth.live.com/token'
+ }
+
+ super(app, :liveid, client_id, client_secret, client_options, options, &block)
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(
+ super,
+ {
+ 'uid' => user_data['id'],
+ 'user_info' => user_info,
+ 'extra' => {
+ 'user_hash' => user_data,
+ }
+ }
+ )
+ end
+
+ def request_phase
+ options[:scope] ||= 'wl.signin wl.basic'
+ options[:response_type] ||= 'code'
+ super
+ end
+
+ def user_data
+ @data ||= MultiJson.decode(@access_token.get('https://apis.live.net/v5.0/me').body)
+ end
+
+ def user_info
+ {
+ 'id' => user_data['id'],
+ 'name' => user_data['name'],
+ 'email' => '',
+ 'first_name' => user_data['first_name'],
+ 'last_name' => user_data['last_name'],
+ 'link' => user_data['link'],
+ 'gender' => user_data['gender'],
+ 'locale' => user_data['locale']
+ }
+ end
+
+ end
+ end
+end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/mailchimp.rb b/oa-oauth/lib/omniauth/strategies/oauth2/mailchimp.rb
new file mode 100644
index 0000000..d0f6b13
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/mailchimp.rb
@@ -0,0 +1,39 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+ class Mailchimp < OmniAuth::Strategies::OAuth2
+
+ def initialize(app, client_id=nil, client_secret=nil, options={}, &block)
+ client_options = {
+ :authorize_url => 'https://login.mailchimp.com/oauth2/authorize',
+ :token_url => 'https://login.mailchimp.com/oauth2/token',
+ }
+ super(app, :mailchimp, client_id, client_secret, client_options, options, &block)
+ end
+
+ def auth_hash
+ data = user_data
+ OmniAuth::Utils.deep_merge(
+ super, {
+ 'uid' => @access_token.client.id,
+ 'extra'=> {
+ 'user_hash' => data
+ }
+ }
+ )
+ end
+
+ def user_data
+ @data ||= MultiJson.decode(@access_token.get("https://login.mailchimp.com/oauth2/metadata").body)
+ rescue ::OAuth2::Error => e
+ if e.response.status == 302
+ @data ||= MultiJson.decode(@access_token.get(e.response.headers['location']))
+ else
+ raise e
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/salesforce.rb b/oa-oauth/lib/omniauth/strategies/oauth2/salesforce.rb
index 930b484..978490a 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth2/salesforce.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/salesforce.rb
@@ -18,7 +18,10 @@ module OmniAuth
super, {
'uid' => @access_token['id'],
'credentials' => {
+ 'issued_at' => @access_token['issued_at'],
+ 'refresh_token' => @access_token.refresh_token,
'instance_url' => @access_token['instance_url'],
+ 'signature' => @access_token['signature'],
},
'extra' => {
'user_hash' => data,
diff --git a/oa-oauth/lib/omniauth/strategies/oauth/sound_cloud.rb b/oa-oauth/lib/omniauth/strategies/oauth2/sound_cloud.rb
similarity index 73%
rename from oa-oauth/lib/omniauth/strategies/oauth/sound_cloud.rb
rename to oa-oauth/lib/omniauth/strategies/oauth2/sound_cloud.rb
index 7bd8dca..233b5cd 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth/sound_cloud.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/sound_cloud.rb
@@ -3,15 +3,17 @@ require 'multi_json'
module OmniAuth
module Strategies
- # Authenticate to SoundCloud via OAuth and retrieve basic
+ # Authenticate to SoundCloud via OAuth2 and retrieve basic
# user information.
#
# Usage:
# use OmniAuth::Strategies::SoundCloud, 'consumerkey', 'consumersecret'
- class SoundCloud < OmniAuth::Strategies::OAuth
+ class SoundCloud < OmniAuth::Strategies::OAuth2
def initialize(app, consumer_key=nil, consumer_secret=nil, options={}, &block)
client_options = {
:site => 'https://api.soundcloud.com',
+ :authorize_url => 'https://soundcloud.com/connect',
+ :token_url => 'https://api.soundcloud.com/oauth2/token'
}
super(app, :soundcloud, consumer_key, consumer_secret, client_options, options, &block)
end
@@ -45,6 +47,14 @@ module OmniAuth
def user_hash
@user_hash ||= MultiJson.decode(@access_token.get('/me.json').body)
end
+
+ # OAuth2 by default uses 'Bearer %s' in the header
+ def build_access_token
+ access_token = super
+ access_token.options[:header_format] = "OAuth %s"
+ access_token
+ end
+
end
end
end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/taobao.rb b/oa-oauth/lib/omniauth/strategies/oauth2/taobao.rb
index 6cef19a..1361772 100644
--- a/oa-oauth/lib/omniauth/strategies/oauth2/taobao.rb
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/taobao.rb
@@ -10,7 +10,7 @@ module OmniAuth
#
# @example Basic Usage
# use OmniAuth::Strategies::TaoBao, 'client_id', 'client_secret'
- class TaoBao < OmniAuth::Strategies::OAuth2
+ class Taobao < OmniAuth::Strategies::OAuth2
# @param [Rack Application] app standard middleware application parameter
# @param [String] client_id the app key at taobao open platform
# @param [String] client_secret the app secret at taobao open platform
@@ -71,7 +71,8 @@ module OmniAuth
end
def generate_sign(params)
- str = client_secret + (params.sort.collect { |k, v| "#{k}#{v}" }).join + client_secret
+ # params.sort.collect { |k, v| "#{k}#{v}" }
+ str = client_secret + params.sort {|a,b| "#{a[0]}"<=>"#{b[0]}"}.flatten.join + client_secret
params['sign'] = Digest::MD5.hexdigest(str).upcase!
params
end
diff --git a/oa-oauth/lib/omniauth/strategies/oauth2/viadeo.rb b/oa-oauth/lib/omniauth/strategies/oauth2/viadeo.rb
new file mode 100644
index 0000000..dd99077
--- /dev/null
+++ b/oa-oauth/lib/omniauth/strategies/oauth2/viadeo.rb
@@ -0,0 +1,72 @@
+require 'omniauth/oauth'
+require 'multi_json'
+
+module OmniAuth
+ module Strategies
+ # Authenticate to Viadeo utilizing OAuth 2.0 and retrieve
+ # basic user information.
+ #
+ # @example Basic Usage
+ # use OmniAuth::Strategies::Viadeo, 'client_id', 'client_secret'
+ class Viadeo < OmniAuth::Strategies::OAuth2
+ # @param [Rack Application] app standard middleware application parameter
+ # @param [String] client_id the application id as [registered on Viadeo](http://dev.viadeo.com/)
+ # @param [String] client_secret the application secret as registered on Facebook
+ def initialize(app, client_id=nil, client_secret=nil, options = {}, &block)
+ client_options = {
+ :site => 'https://api.viadeo.com/',
+ :authorize_url => 'https://secure.viadeo.com/oauth-provider/authorize2',
+ :token_url => 'https://secure.viadeo.com/oauth-provider/access_token2'
+ }
+ super(app, :viadeo, client_id, client_secret, client_options, options, &block)
+ end
+
+ def auth_hash
+ OmniAuth::Utils.deep_merge(
+ super, {
+ 'uid' => user_data['id'],
+ 'user_info' => user_info,
+ 'extra' => {
+ 'user_hash' => user_data,
+ },
+ }
+ )
+ end
+
+ def user_data
+ @data ||= MultiJson.decode(@access_token.get('/me').body)
+ end
+
+ def request_phase
+ options[:response_type] ||= 'code'
+ super
+ end
+
+ def callback_phase
+ options[:grant_type] ||= 'authorization_code'
+ super
+ end
+
+ def user_info
+ {
+ 'name' => user_data['name'],
+ 'link' => user_data['link'],
+ 'first_name' => user_data['first_name'],
+ 'last_name' => user_data['last_name'],
+ 'gender' => user_data['gender'],
+ 'nickname' => user_data['nickname'],
+ 'has_picture' => user_data['has_picture'] ,
+ 'picture_small' => user_data['picture_small'],
+ 'picture_large' => user_data['picture_large'],
+ 'headline' => user_data['headline'],
+ 'introduction' => user_data['introduction'],
+ 'interests' => user_data['interests'],
+ 'location' => user_data['location'],
+ 'is_premium' => user_data['is_premium'],
+ 'premium_since' => user_data['premium_since']
+ }
+ end
+
+ end
+ end
+end
diff --git a/oa-oauth/oa-oauth.gemspec b/oa-oauth/oa-oauth.gemspec
index 3e36edf..89c0149 100644
--- a/oa-oauth/oa-oauth.gemspec
+++ b/oa-oauth/oa-oauth.gemspec
@@ -1,33 +1,31 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
- gem.add_runtime_dependency 'faraday', '~> 0.7.3'
- gem.add_runtime_dependency 'multi_json', '~> 1.0.0'
- gem.add_runtime_dependency 'multi_xml', '~> 0.2.2'
- gem.add_runtime_dependency 'oa-core', OmniAuth::Version::STRING
- gem.add_runtime_dependency 'oauth', '~> 0.4.0'
- gem.add_runtime_dependency 'oauth2', '~> 0.5.0'
+ gem.add_dependency 'faraday', '~> 0.7.3'
+ gem.add_dependency 'multi_json', '~> 1.0.0'
+ gem.add_dependency 'multi_xml', '~> 0.3.0'
+ gem.add_dependency 'oa-core', OmniAuth::Version::STRING
+ gem.add_dependency 'oauth', '~> 0.4.0'
+ gem.add_dependency 'oauth2', '~> 0.5.0'
gem.add_development_dependency 'evernote', '~> 1.0'
- gem.add_development_dependency 'maruku', '~> 0.6'
gem.add_development_dependency 'rack-test', '~> 0.5'
gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rdiscount', '~> 1.6'
gem.add_development_dependency 'rspec', '~> 2.5'
gem.add_development_dependency 'simplecov', '~> 0.4'
- gem.add_development_dependency 'webmock', '~> 1.6'
- gem.add_development_dependency 'yard', '~> 0.7'
gem.add_development_dependency 'vcr', '~> 1.10'
- gem.add_development_dependency 'ZenTest', '~> 4.5'
- gem.name = 'oa-oauth'
- gem.version = OmniAuth::Version::STRING
- gem.description = %q{OAuth strategies for OmniAuth.}
- gem.summary = gem.description
- gem.email = ['michael@intridea.com', 'sferik@gmail.com']
- gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.add_development_dependency 'webmock', '~> 1.7'
+ gem.add_development_dependency 'yard', '~> 0.7'
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.description = %q{OAuth strategies for OmniAuth.}
+ gem.email = ['michael@intridea.com', 'sferik@gmail.com']
gem.files = `git ls-files`.split("\n")
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.name = 'oa-oauth'
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
+ gem.summary = gem.description
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.version = OmniAuth::Version::STRING
end
diff --git a/oa-oauth/spec/omniauth/strategies/google_oauth2_spec.rb b/oa-oauth/spec/omniauth/strategies/google_oauth2_spec.rb
new file mode 100644
index 0000000..7ab0f84
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/google_oauth2_spec.rb
@@ -0,0 +1,5 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe OmniAuth::Strategies::GoogleOAuth2 do
+ it_should_behave_like "an oauth2 strategy"
+end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth/flickr_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth/flickr_spec.rb
new file mode 100644
index 0000000..98944a3
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth/flickr_spec.rb
@@ -0,0 +1,6 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Flickr do
+ it_should_behave_like "an oauth strategy"
+end
+
diff --git a/oa-oauth/spec/omniauth/strategies/oauth/oauth_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth/oauth_spec.rb
index b8f8072..9d09a2c 100644
--- a/oa-oauth/spec/omniauth/strategies/oauth/oauth_spec.rb
+++ b/oa-oauth/spec/omniauth/strategies/oauth/oauth_spec.rb
@@ -57,6 +57,19 @@ describe "OmniAuth::Strategies::OAuth" do
last_request.env['omniauth.error'].should be_kind_of(::Net::HTTPFatalError)
last_request.env['omniauth.error.type'] = :service_unavailable
end
+
+ context "SSL failure" do
+ before do
+ stub_request(:post, 'https://api.example.org/oauth/request_token').
+ to_raise(::OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed"))
+ get '/auth/example.org'
+ end
+
+ it 'should call fail! with :service_unavailable' do
+ last_request.env['omniauth.error'].should be_kind_of(::OpenSSL::SSL::SSLError)
+ last_request.env['omniauth.error.type'] = :service_unavailable
+ end
+ end
end
end
@@ -88,6 +101,19 @@ describe "OmniAuth::Strategies::OAuth" do
last_request.env['omniauth.error.type'] = :service_unavailable
end
end
+
+ context "SSL failure" do
+ before do
+ stub_request(:post, 'https://api.example.org/oauth/access_token').
+ to_raise(::OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed"))
+ get '/auth/example.org/callback', {:oauth_verifier => 'dudeman'}, {'rack.session' => {'oauth' => {"example.org" => {'callback_confirmed' => true, 'request_token' => 'yourtoken', 'request_secret' => 'yoursecret'}}}}
+ end
+
+ it 'should call fail! with :service_unavailable' do
+ last_request.env['omniauth.error'].should be_kind_of(::OpenSSL::SSL::SSLError)
+ last_request.env['omniauth.error.type'] = :service_unavailable
+ end
+ end
end
describe '/auth/{name}/callback with expired session' do
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/angellist_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/angellist_spec.rb
new file mode 100644
index 0000000..e396d4a
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/angellist_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::AngelList do
+ it_should_behave_like "an oauth2 strategy"
+end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/instagram_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/instagram_spec.rb
new file mode 100644
index 0000000..2a00ec7
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/instagram_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Instagram do
+ it_should_behave_like "an oauth2 strategy"
+end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/liveid_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/liveid_spec.rb
new file mode 100644
index 0000000..6357c30
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/liveid_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Liveid do
+ it_should_behave_like 'an oauth2 strategy'
+end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/mailchimp_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/mailchimp_spec.rb
new file mode 100644
index 0000000..2ea822d
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/mailchimp_spec.rb
@@ -0,0 +1,6 @@
+# oa-oauth/spec/omniauth/strategies/rdio_spec.rb
+require File.expand_path('../../../../spec_helper', __FILE__)
+
+describe OmniAuth::Strategies::Mailchimp do
+ it_should_behave_like "an oauth2 strategy"
+end
\ No newline at end of file
diff --git a/oa-oauth/spec/omniauth/strategies/oauth/sound_cloud_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/sound_cloud_spec.rb
similarity index 61%
rename from oa-oauth/spec/omniauth/strategies/oauth/sound_cloud_spec.rb
rename to oa-oauth/spec/omniauth/strategies/oauth2/sound_cloud_spec.rb
index 469b741..df47c0e 100644
--- a/oa-oauth/spec/omniauth/strategies/oauth/sound_cloud_spec.rb
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/sound_cloud_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe OmniAuth::Strategies::SoundCloud do
- it_should_behave_like 'an oauth strategy'
+ it_should_behave_like "an oauth2 strategy"
end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/taobao_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/taobao_spec.rb
index d846f61..f995879 100644
--- a/oa-oauth/spec/omniauth/strategies/oauth2/taobao_spec.rb
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/taobao_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
-describe OmniAuth::Strategies::TaoBao do
+describe OmniAuth::Strategies::Taobao do
it_should_behave_like "an oauth2 strategy"
end
diff --git a/oa-oauth/spec/omniauth/strategies/oauth2/viadeo_spec.rb b/oa-oauth/spec/omniauth/strategies/oauth2/viadeo_spec.rb
new file mode 100644
index 0000000..d692987
--- /dev/null
+++ b/oa-oauth/spec/omniauth/strategies/oauth2/viadeo_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Viadeo do
+ it_should_behave_like "an oauth2 strategy"
+end
diff --git a/oa-openid/lib/omniauth/strategies/open_id.rb b/oa-openid/lib/omniauth/strategies/open_id.rb
index be75c64..a1400f4 100644
--- a/oa-openid/lib/omniauth/strategies/open_id.rb
+++ b/oa-openid/lib/omniauth/strategies/open_id.rb
@@ -56,9 +56,11 @@ module OmniAuth
end
def identifier
- options[:identifier] || request[IDENTIFIER_URL_PARAMETER]
+ i = options[:identifier] || request[IDENTIFIER_URL_PARAMETER]
+ i = nil if i == ''
+ i
end
-
+
def request_phase
identifier ? start : get_identifier
end
diff --git a/oa-openid/oa-openid.gemspec b/oa-openid/oa-openid.gemspec
index 522f80b..ff87433 100644
--- a/oa-openid/oa-openid.gemspec
+++ b/oa-openid/oa-openid.gemspec
@@ -1,28 +1,26 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
- gem.add_runtime_dependency 'oa-core', OmniAuth::Version::STRING
- gem.add_runtime_dependency 'rack-openid', '~> 1.3.1'
- gem.add_runtime_dependency 'ruby-openid-apps-discovery', '~> 1.2.0'
- gem.add_development_dependency 'maruku', '~> 0.6'
+ gem.add_dependency 'oa-core', OmniAuth::Version::STRING
+ gem.add_dependency 'rack-openid', '~> 1.3.1'
+ gem.add_dependency 'ruby-openid-apps-discovery', '~> 1.2.0'
gem.add_development_dependency 'rack-test', '~> 0.5'
gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rdiscount', '~> 1.6'
gem.add_development_dependency 'rspec', '~> 2.5'
gem.add_development_dependency 'simplecov', '~> 0.4'
- gem.add_development_dependency 'webmock', '~> 1.6'
+ gem.add_development_dependency 'webmock', '~> 1.7'
gem.add_development_dependency 'yard', '~> 0.7'
- gem.add_development_dependency 'ZenTest', '~> 4.5'
- gem.name = 'oa-openid'
- gem.version = OmniAuth::Version::STRING
- gem.description = %q{OpenID strategies for OmniAuth.}
- gem.summary = gem.description
- gem.email = ['michael@intridea.com', 'sferik@gmail.com']
- gem.homepage = 'http://github.com/intridea/omniauth'
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
- gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.description = %q{OpenID strategies for OmniAuth.}
+ gem.email = ['michael@intridea.com', 'sferik@gmail.com']
gem.files = `git ls-files`.split("\n")
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.name = 'oa-openid'
gem.require_paths = ['lib']
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
+ gem.summary = gem.description
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.version = OmniAuth::Version::STRING
end
diff --git a/omniauth.gemspec b/omniauth.gemspec
index 66a0536..e3e7bc4 100644
--- a/omniauth.gemspec
+++ b/omniauth.gemspec
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# encoding: utf-8
require File.expand_path('../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency subgem, OmniAuth::Version::STRING
end
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
- 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.description = %q{OmniAuth is an authentication framework 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', 'sferik@gmail.com']
gem.files = Dir.glob("lib/**/*") + %w(README.md LICENSE.md)
gem.homepage = 'http://github.com/intridea/omniauth'
diff --git a/spec/omniauth/builder_spec.rb b/spec/omniauth/builder_spec.rb
new file mode 100644
index 0000000..c45c3c2
--- /dev/null
+++ b/spec/omniauth/builder_spec.rb
@@ -0,0 +1,20 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe OmniAuth::Builder do
+ describe '#provider' do
+ it 'should translate a symbol to a constant' do
+ OmniAuth::Strategies.should_receive(:const_get).with('MyStrategy').and_return(Class.new)
+ OmniAuth::Builder.new(nil) do
+ provider :my_strategy
+ end
+ end
+
+ it 'should also just accept a class' do
+ class ::ExampleClass; end
+
+ lambda{ OmniAuth::Builder.new(nil) do
+ provider ::ExampleClass
+ end }.should_not raise_error
+ end
+ end
+end
diff --git a/spec/omniauth/core_spec.rb b/spec/omniauth/core_spec.rb
new file mode 100644
index 0000000..dffa5ce
--- /dev/null
+++ b/spec/omniauth/core_spec.rb
@@ -0,0 +1,79 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe OmniAuth do
+ describe '.strategies' do
+ it 'should increase when a new strategy is made' do
+ lambda{ class ExampleStrategy
+ include OmniAuth::Strategy
+ end }.should change(OmniAuth.strategies, :size).by(1)
+ OmniAuth.strategies.last.should == ExampleStrategy
+ end
+ end
+
+ context 'configuration' do
+ it 'should be callable from .configure' do
+ OmniAuth.configure do |c|
+ c.should be_kind_of(OmniAuth::Configuration)
+ end
+ end
+
+ before do
+ @old_path_prefix = OmniAuth.config.path_prefix
+ @old_on_failure = OmniAuth.config.on_failure
+ end
+
+ after do
+ OmniAuth.configure do |config|
+ config.path_prefix = @old_path_prefix
+ config.on_failure = @old_on_failure
+ end
+ end
+
+ it 'should be able to set the path' do
+ OmniAuth.configure do |config|
+ config.path_prefix = '/awesome'
+ end
+
+ OmniAuth.config.path_prefix.should == '/awesome'
+ end
+
+ it 'should be able to set the on_failure rack app' do
+ OmniAuth.configure do |config|
+ config.on_failure do
+ 'yoyo'
+ end
+ end
+
+ OmniAuth.config.on_failure.call.should == 'yoyo'
+ end
+ end
+
+ describe '::Utils' do
+ describe '.deep_merge' do
+ it 'should combine hashes' do
+ OmniAuth::Utils.deep_merge({'abc' => {'def' => 123}}, {'abc' => {'foo' => 'bar'}}).should == {
+ 'abc' => {'def' => 123, 'foo' => 'bar'}
+ }
+ end
+ end
+
+ describe '.camelize' do
+ it 'should work on normal cases' do
+ {
+ 'some_word' => 'SomeWord',
+ 'AnotherWord' => 'AnotherWord',
+ 'one' => 'One',
+ 'three_words_now' => 'ThreeWordsNow'
+ }.each_pair{ |k,v| OmniAuth::Utils.camelize(k).should == v }
+ end
+
+ it 'should work in special cases' do
+ {
+ 'oauth' => "OAuth",
+ 'openid' => 'OpenID',
+ 'open_id' => 'OpenID'
+ }.each_pair{ |k,v| OmniAuth::Utils.camelize(k).should == v}
+ end
+ end
+ end
+end
diff --git a/spec/omniauth/strategy_spec.rb b/spec/omniauth/strategy_spec.rb
new file mode 100644
index 0000000..3b0a19c
--- /dev/null
+++ b/spec/omniauth/strategy_spec.rb
@@ -0,0 +1,368 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+class ExampleStrategy
+ include OmniAuth::Strategy
+ def call(env); self.call!(env) end
+ attr_reader :last_env
+ def request_phase
+ @fail = fail!(options[:failure]) if options[:failure]
+ @last_env = env
+ return @fail if @fail
+ raise "Request Phase"
+ end
+ def callback_phase
+ @fail = fail!(options[:failure]) if options[:failure]
+ @last_env = env
+ return @fail if @fail
+ raise "Callback Phase"
+ end
+end
+
+def make_env(path = '/auth/test', props = {})
+ {
+ 'REQUEST_METHOD' => 'GET',
+ 'PATH_INFO' => path,
+ 'rack.session' => {},
+ 'rack.input' => StringIO.new('test=true')
+ }.merge(props)
+end
+
+describe OmniAuth::Strategy do
+ let(:app){ lambda{|env| [404, {}, ['Awesome']]}}
+ describe '#initialize' do
+ context 'options extraction' do
+ it 'should be the last argument if the last argument is a Hash' do
+ ExampleStrategy.new(app, 'test', :abc => 123).options[:abc].should == 123
+ end
+
+ it 'should be a blank hash if none are provided' do
+ ExampleStrategy.new(app, 'test').options.should == {}
+ end
+ end
+ end
+
+ describe '#full_host' do
+ let(:strategy){ ExampleStrategy.new(app, 'test', {}) }
+ it 'should not freak out if there is a pipe in the URL' do
+ strategy.call!(make_env('/whatever', 'rack.url_scheme' => 'http', 'SERVER_NAME' => 'facebook.lame', 'QUERY_STRING' => 'code=asofibasf|asoidnasd', 'SCRIPT_NAME' => '', 'SERVER_PORT' => 80))
+ lambda{ strategy.full_host }.should_not raise_error
+ end
+ end
+
+ describe '#call' do
+ let(:strategy){ ExampleStrategy.new(app, 'test', @options) }
+
+ context 'omniauth.origin' do
+ it 'should be set on the request phase' do
+ lambda{ strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin')) }.should raise_error("Request Phase")
+ strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
+ end
+
+ it 'should be turned into an env variable on the callback phase' do
+ lambda{ strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'})) }.should raise_error("Callback Phase")
+ strategy.last_env['omniauth.origin'].should == 'http://example.com/origin'
+ end
+
+ it 'should set from the params if provided' do
+ lambda{ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo')) }.should raise_error('Request Phase')
+ strategy.last_env['rack.session']['omniauth.origin'].should == '/foo'
+ end
+
+ it 'should be set on the failure env' do
+ OmniAuth.config.should_receive(:on_failure).and_return(lambda{|env| env})
+ @options = {:failure => :forced_fail}
+ strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => '/awesome'}))
+ end
+
+ context "with script_name" do
+ it 'should be set on the request phase, containing full path' do
+ env = {'HTTP_REFERER' => 'http://example.com/sub_uri/origin', 'SCRIPT_NAME' => '/sub_uri' }
+ lambda{ strategy.call(make_env('/auth/test', env)) }.should raise_error("Request Phase")
+ strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
+ end
+
+ it 'should be turned into an env variable on the callback phase, containing full path' do
+ env = {
+ 'rack.session' => {'omniauth.origin' => 'http://example.com/sub_uri/origin'},
+ 'SCRIPT_NAME' => '/sub_uri'
+ }
+
+ lambda{ strategy.call(make_env('/auth/test/callback', env)) }.should raise_error("Callback Phase")
+ strategy.last_env['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
+ end
+
+ end
+ end
+
+ context 'default paths' do
+ it 'should use the default request path' do
+ lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
+ end
+
+ it 'should be case insensitive on request path' do
+ lambda{ strategy.call(make_env('/AUTH/Test'))}.should raise_error("Request Phase")
+ end
+
+ it 'should be case insensitive on callback path' do
+ lambda{ strategy.call(make_env('/AUTH/TeSt/CaLlBAck'))}.should raise_error("Callback Phase")
+ end
+
+ it 'should use the default callback path' do
+ lambda{ strategy.call(make_env('/auth/test/callback')) }.should raise_error("Callback Phase")
+ end
+
+ it 'should strip trailing spaces on request' do
+ lambda{ strategy.call(make_env('/auth/test/')) }.should raise_error("Request Phase")
+ end
+
+ it 'should strip trailing spaces on callback' do
+ lambda{ strategy.call(make_env('/auth/test/callback/')) }.should raise_error("Callback Phase")
+ end
+
+ context 'callback_url' do
+ it 'uses the default callback_path' do
+ strategy.should_receive(:full_host).and_return('http://example.com')
+
+ lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
+
+ strategy.callback_url.should == 'http://example.com/auth/test/callback'
+ end
+
+ it 'preserves the query parameters' do
+ strategy.stub(:full_host).and_return('http://example.com')
+ begin
+ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
+ rescue RuntimeError; end
+ strategy.callback_url.should == 'http://example.com/auth/test/callback?id=5'
+ end
+
+ it 'consider script name' do
+ strategy.stub(:full_host).and_return('http://example.com')
+ begin
+ strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
+ rescue RuntimeError; end
+ strategy.callback_url.should == 'http://example.com/sub_uri/auth/test/callback'
+ end
+ end
+ end
+
+ context 'pre-request call through' do
+ subject { ExampleStrategy.new(app, 'test') }
+ let(:app){ lambda{|env| env['omniauth.boom'] = true; [env['test.status'] || 404, {}, ['Whatev']] } }
+ it 'should be able to modify the env on the fly before the request_phase' do
+ lambda{ subject.call(make_env) }.should raise_error("Request Phase")
+ subject.response.status.should == 404
+ subject.last_env.should be_key('omniauth.boom')
+ end
+
+ it 'should call through to the app instead if a non-404 response is received' do
+ lambda{ subject.call(make_env('/auth/test', 'test.status' => 200)) }.should_not raise_error
+ subject.response.body.should == ['Whatev']
+ end
+ end
+
+ context 'custom paths' do
+ it 'should use a custom request_path if one is provided' do
+ @options = {:request_path => '/awesome'}
+ lambda{ strategy.call(make_env('/awesome')) }.should raise_error("Request Phase")
+ end
+
+ it 'should use a custom callback_path if one is provided' do
+ @options = {:callback_path => '/radical'}
+ lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
+ end
+
+ context 'callback_url' do
+ it 'uses a custom callback_path if one is provided' do
+ @options = {:callback_path => '/radical'}
+ strategy.should_receive(:full_host).and_return('http://example.com')
+
+ lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
+
+ strategy.callback_url.should == 'http://example.com/radical'
+ end
+
+ it 'preserves the query parameters' do
+ @options = {:callback_path => '/radical'}
+ strategy.stub(:full_host).and_return('http://example.com')
+ begin
+ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
+ rescue RuntimeError; end
+ strategy.callback_url.should == 'http://example.com/radical?id=5'
+ end
+ end
+ end
+
+ context 'custom prefix' do
+ before do
+ @options = {:path_prefix => '/wowzers'}
+ end
+
+ it 'should use a custom prefix for request' do
+ lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
+ end
+
+ it 'should use a custom prefix for callback' do
+ lambda{ strategy.call(make_env('/wowzers/test/callback')) }.should raise_error("Callback Phase")
+ end
+
+ context 'callback_url' do
+ it 'uses a custom prefix' do
+ strategy.should_receive(:full_host).and_return('http://example.com')
+
+ lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
+
+ strategy.callback_url.should == 'http://example.com/wowzers/test/callback'
+ end
+
+ it 'preserves the query parameters' do
+ strategy.stub(:full_host).and_return('http://example.com')
+ begin
+ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
+ rescue RuntimeError; end
+ strategy.callback_url.should == 'http://example.com/wowzers/test/callback?id=5'
+ end
+ end
+ end
+
+ context 'request method restriction' do
+ before do
+ OmniAuth.config.allowed_request_methods = [:post]
+ end
+
+ it 'should not allow a request method of the wrong type' do
+ lambda{ strategy.call(make_env)}.should_not raise_error
+ end
+
+ it 'should allow a request method of the correct type' do
+ lambda{ strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'POST'))}.should raise_error("Request Phase")
+ end
+
+ after do
+ OmniAuth.config.allowed_request_methods = [:get, :post]
+ end
+ end
+
+ context 'test mode' do
+ before do
+ OmniAuth.config.test_mode = true
+ end
+
+ it 'should short circuit the request phase entirely' do
+ response = strategy.call(make_env)
+ response[0].should == 302
+ response[1]['Location'].should == '/auth/test/callback'
+ end
+
+ it 'should be case insensitive on request path' do
+ strategy.call(make_env('/AUTH/Test'))[0].should == 302
+ end
+
+ it 'should respect SCRIPT_NAME (a.k.a. BaseURI)' do
+ response = strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
+ response[1]['Location'].should == '/sub_uri/auth/test/callback'
+ end
+
+ it 'should be case insensitive on callback path' do
+ strategy.call(make_env('/AUTH/TeSt/CaLlBAck')).should == strategy.call(make_env('/auth/test/callback'))
+ end
+
+ it 'should maintain query string parameters' do
+ response = strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'cheese=stilton'))
+ response[1]['Location'].should == '/auth/test/callback?cheese=stilton'
+ end
+
+ it 'should not short circuit requests outside of authentication' do
+ strategy.call(make_env('/')).should == app.call(make_env('/'))
+ end
+
+ it 'should respond with the default hash if none is set' do
+ strategy.call make_env('/auth/test/callback')
+ strategy.env['omniauth.auth']['uid'].should == '1234'
+ end
+
+ it 'should respond with a provider-specific hash if one is set' do
+ OmniAuth.config.mock_auth[:test] = {
+ 'uid' => 'abc'
+ }
+
+ strategy.call make_env('/auth/test/callback')
+ strategy.env['omniauth.auth']['uid'].should == 'abc'
+ end
+
+ it 'should simulate login failure if mocked data is set as a symbol' do
+ OmniAuth.config.mock_auth[:test] = :invalid_credentials
+
+ strategy.call make_env('/auth/test/callback')
+ strategy.env['omniauth.error.type'].should == :invalid_credentials
+ end
+
+ it 'should set omniauth.origin on the request phase' do
+ strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin'))
+ strategy.env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
+ end
+
+ it 'should set omniauth.origin from the params if provided' do
+ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo'))
+ strategy.env['rack.session']['omniauth.origin'].should == '/foo'
+ end
+
+ it 'should turn omniauth.origin into an env variable on the callback phase' do
+ OmniAuth.config.mock_auth[:test] = {}
+
+ strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'}))
+ strategy.env['omniauth.origin'].should == 'http://example.com/origin'
+ end
+ end
+
+ context 'custom full_host' do
+ it 'should be the string when a string is there' do
+ OmniAuth.config.full_host = 'my.host.com'
+ strategy.full_host.should == 'my.host.com'
+ end
+
+ it 'should run the proc with the env when it is a proc' do
+ OmniAuth.config.full_host = Proc.new{|env| env['HOST']}
+ strategy.call(make_env('/auth/test', 'HOST' => 'my.host.net'))
+ strategy.full_host.should == 'my.host.net'
+ end
+ end
+ end
+
+ context 'setup phase' do
+ context 'when options[:setup] = true' do
+ let(:strategy){ ExampleStrategy.new(app, 'test', :setup => true) }
+ let(:app){lambda{|env| env['omniauth.strategy'].options[:awesome] = 'sauce' if env['PATH_INFO'] == '/auth/test/setup'; [404, {}, 'Awesome'] }}
+
+ it 'should call through to /auth/:provider/setup' do
+ strategy.call(make_env('/auth/test'))
+ strategy.options[:awesome].should == 'sauce'
+ end
+
+ it 'should not call through on a non-omniauth endpoint' do
+ strategy.call(make_env('/somewhere/else'))
+ strategy.options[:awesome].should_not == 'sauce'
+ end
+ end
+
+ context 'when options[:setup] is an app' do
+ let(:setup_proc) do
+ Proc.new do |env|
+ env['omniauth.strategy'].options[:awesome] = 'sauce'
+ end
+ end
+
+ let(:strategy){ ExampleStrategy.new(app, 'test', :setup => setup_proc) }
+
+ it 'should not call the app on a non-omniauth endpoint' do
+ strategy.call(make_env('/somehwere/else'))
+ strategy.options[:awesome].should_not == 'sauce'
+ end
+
+ it 'should call the rack app' do
+ strategy.call(make_env('/auth/test'))
+ strategy.options[:awesome].should == 'sauce'
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..c9f3ecf
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,12 @@
+require 'simplecov'
+SimpleCov.start
+require 'rspec'
+require 'rack/test'
+require 'omniauth/core'
+require 'omniauth/test'
+
+RSpec.configure do |config|
+ config.include Rack::Test::Methods
+ config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
+end
+