diff --git a/README.markdown b/README.markdown index 0109c4d..ec6d213 100644 --- a/README.markdown +++ b/README.markdown @@ -11,31 +11,48 @@ By using WardenOmniAuth, you can make use of any of the OmniAuth authentication This is also usable in the [Devise](http://github.com/plataformatec/devise) Rails Engine ## Usage (Rack) -
use OmniAuth::Builer do
+```ruby
+use OmniAuth::Builer do
# setup omniauth
end
+OmniAuth.config.on_failure = Proc.new do |env|
+ OmniAuth::FailureEndpoint.new(env).redirect_to_failure
+end
+
use Warden::Manager do |config|
- # setup warden configuration
+ # setup warden configuration, e.g.:
+ config.failure_app = lambda do |env|
+ # This is also the failure app for any OmniAuth failures
+ Rack::Response.new({:errors => env['warden'].errors.full_messages}.to_json, 401).finish
+ end
+e
end
use WardenOmniAuth do |config|
- config.redirect\_after\_callback = "/redirect/path" # default "/"
+ config.redirect_after_callback { |env| env['omniauth.origin'] || "/redirect/path" }
+ # or: config.redirect_after_callback = "/redirect/path"
+end
+
+WardenOmniAuth.on_callback do |omni_user, strategy|
+ # return a user object, e.g.:
+ User.authenticate!(strategy, omni_user['uid'])
end
run MyApp
-
+```
## Usage (Devise)
-# config/initializer.rb
+```ruby
+# config/initializer.rb
Devise.setup do |config|
config.warden do |manager|
- [:omni\_twitter, :omni\_facebook, :omni\_github].each do |strategy|
- manager.default\_strategies(:scope => :user).unshift strategy
+ [:omni_twitter, :omni_facebook, :omni_github].each do |strategy|
+ manager.default_strategies(:scope => :user).unshift strategy
end
end
-
+```
This will add the stratgeies to the normal devise user login for github, then facebook, then twitter.
@@ -47,27 +64,28 @@ By default, it grabs just _user\\info_, _uid_, _credentials_, _provider_ as a ha
If you want to customise this you can do:
-
- WardenOmniAuth.on\_callback do |user|
- # all callbacks will go here by default
+```ruby
+ WardenOmniAuth.on_callback do |user,strategy|
+ # all callbacks will go here by default;
+ # strategy is something like 'twitter', 'facebook', etc
end
-
+```
Whatever you return from the block is the user that's made available in warden.
## Dealing with each kind of callback
-
+```ruby
use WardenOmniAuth do |config|
- Warden::Strategies[:omni\_twitter].on_callback do |user|
+ Warden::Strategies[:omni_twitter].on_callback do |user,strategy|
# do stuff to get a user and return it from the block
end
- Warden::Strategies[:omni\_facebook].on_callback do |user|
+ Warden::Strategies[:omni_facebook].on_callback do |user,strategy|
# do stuff to get a user for a facebook user
end
end
-
+```
This will use a specific callback to get the user, or fallback if nothing specific has been defined.
diff --git a/lib/warden_omniauth.rb b/lib/warden_omniauth.rb
index bfe4ecc..a4b4afc 100644
--- a/lib/warden_omniauth.rb
+++ b/lib/warden_omniauth.rb
@@ -2,7 +2,7 @@
require 'omniauth'
class WardenOmniAuth
- DEFAULT_CALLBACK = lambda do |user|
+ DEFAULT_CALLBACK = lambda do |user, strategy|
u = {}
u[:info] = user['info']
u[:uid] = user['uid']
@@ -30,7 +30,7 @@ def self.on_callback(&blk)
# @example
# WardenOmniAuth.setup_strategies(:twitter, :facebook)
def self.setup_strategies(*names)
- names.map do |name|
+ names.each do |name|
full_name = :"omni_#{name}"
unless Warden::Strategies[full_name]
klass = Class.new(WardenOmniAuth::Strategy)
@@ -44,20 +44,15 @@ def self.setup_strategies(*names)
# The base omniauth warden strategy. This is inherited for each
# omniauth strategy
class Strategy < Warden::Strategies::Base
- # make a specific callback for this strategy
- def self.on_callback(&blk)
- @on_callback = blk if blk
- @on_callback || WardenOmniAuth.on_callback
- end
-
- # The name of the OmniAuth strategy to map to
- def self.omni_name=(name)
- @omni_name = name
- end
-
- # The name of the OmniAuth strategy to map to
- def self.omni_name
- @omni_name
+ class << self
+ # The name of the OmniAuth strategy to map to
+ attr_accessor :omni_name
+
+ # make a specific callback for this strategy
+ def on_callback(&blk)
+ @on_callback = blk if blk
+ @on_callback || WardenOmniAuth.on_callback
+ end
end
def authenticate!
@@ -66,51 +61,38 @@ def authenticate!
# set the user if one exists
# otherwise, redirect for authentication
- if user = (env['omniauth.auth'] || env['rack.auth'] || request['auth']) # TODO: Fix.. Completely insecure... do not use this will look in params for the auth. Apparently fixed in the new gem
-
- success! self.class.on_callback[user]
+ if user = env['omniauth.auth']
+ success! self.class.on_callback[user, self.class.omni_name]
else
path_prefix = OmniAuth::Configuration.instance.path_prefix
- redirect! File.join(path_prefix, self.class.omni_name)
+ # Pass in the request URI as the 'origin' param, so that
+ # OmniAuth later provides it under env['omniauth.origin']
+ redirect! File.join(path_prefix, self.class.omni_name), { 'origin' => env['REQUEST_URI'] }
end
end
end
- # Pulled from extlib
- # Convert to snake case.
- #
- # "FooBar".snake_case #=> "foo_bar"
- # "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
- # "CNN".snake_case #=> "cnn"
- #
- # @return [String] Receiver converted to snake case.
- #
- # @api public
- def self.snake_case(string)
- return string.downcase if string.match(/\A[A-Z]+\z/)
- string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
- gsub(/([a-z])([A-Z])/, '\1_\2').
- downcase
- end
-
def initialize(app)
# setup the warden strategies to wrap the omniauth ones
names = OmniAuth::Strategies.constants.map do |konstant|
- name = WardenOmniAuth.snake_case(konstant.to_s)
+ # Create a fake instance of the OmniAuth strategy to be able
+ # to use the #name method instead of guessing based on the
+ # class name.
+ s = OmniAuth::Strategies.const_get(konstant).new(nil)
+ s.name
end
WardenOmniAuth.setup_strategies(*names)
yield self if block_given?
@app = app
end
- # redirect after a callback
def redirect_after_callback=(path)
- @redirect_after_callback_path = path
+ @redirect_after_callback = lambda { |env| path }
end
-
- def redirect_after_callback_path
- @redirect_after_callback_path ||= "/"
+ # redirect after a callback
+ def redirect_after_callback(&block)
+ @redirect_after_callback = block
end
def call(env)
@@ -132,7 +114,7 @@ def call(env)
args << {:scope => scope.to_sym} if scope
response = Rack::Response.new
if env['warden'].authenticate? *args
- response.redirect(redirect_after_callback_path)
+ response.redirect(@redirect_after_callback.call(env).to_s)
response.finish
else
auth_path = request.path.gsub(/\/callback$/, "")
@@ -143,6 +125,10 @@ def call(env)
Rack::Response.new("Bad Session", 400).finish
end
end
+ elsif request.path =~ /^#{prefix}\/failure$/i
+ # query params: message, strategy, origin
+ env['warden'].errors.add(:login, request.params['message'])
+ throw :warden
else
@app.call(env)
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index e639a2c..4479a4b 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -15,14 +15,20 @@
user
end
-
module MyHelpers
def app
@app || create_app{|e| Rack::Response.new("OK").finish }
end
def create_app(&blk)
- failure = lambda{|e| Rack::Response.new("Can't login", 401).finish }
+ failure = lambda do |env|
+ errors = env['warden'].errors.full_messages
+ if errors.count > 0
+ Rack::Response.new("Can't login: #{errors.join(',')}", 401).finish
+ else
+ Rack::Response.new("Can't login", 401).finish
+ end
+ end
builder = Rack::Builder.new do
use Warden::Manager do |config|
config.failure_app = failure
@@ -33,7 +39,8 @@ def create_app(&blk)
use WardenOmniAuth do |config|
$omni_auth = config
- config.redirect_after_callback = "/redirect/path"
+ $expected_redirect = "/redirect/path"
+ config.redirect_after_callback = $expected_redirect
end
run blk
end.to_app
diff --git a/test/test_warden_omniauth.rb b/test/test_warden_omniauth.rb
index f44c200..871fe90 100644
--- a/test/test_warden_omniauth.rb
+++ b/test/test_warden_omniauth.rb
@@ -9,11 +9,11 @@
include MyHelpers
teardown { @_rack_mock_sessions = nil; @_rack_test_sessions = nil }
- # shoudl setup all the omni auth strategies
+ # should setup all the omni auth strategies
test do
app
OmniAuth::Strategies.constants.each do |klass|
- name = WardenOmniAuth.snake_case(klass.to_s)
+ name = OmniAuth::Strategies.const_get(klass).new(nil).name
assert { Warden::Strategies[:"omni_#{name}"] != nil }
assert { Warden::Strategies[:"omni_#{name}"].superclass == WardenOmniAuth::Strategy }
end
@@ -43,7 +43,7 @@
assert { response.body.to_s == "/auth/twitter" }
end
- # the callback url shoudl be intercepted and should raise if it's unknown
+ # the callback url should be intercepted and should raise if it's unknown
test do
assert { Warden::Strategies[:omni_does_not_exist].nil? }
response = get "/auth/does_not_exist/callback", {}, {'rack.session' => {}}
@@ -54,7 +54,7 @@
# the callback url should be intercepted and should redirect back to the strategy if there is no user
# in rack['auth']
test do
- response = get "/auth/twitter/callback", {}, { 'rack.auth' => nil, 'rack.session' => {}}
+ response = get "/auth/twitter/callback", {}, { 'omniauth.auth' => nil, 'rack.session' => {}}
assert("status should be 302") { response.status == 302 }
assert("url should be /auth/twitter") { response.headers['Location'] == '/auth/twitter' }
end
@@ -68,6 +68,44 @@
assert("status should be 400" ) { response.status == 400 }
assert("body should be bad status" ) { response.body.to_s == "Bad Session" }
end
+
+ # The failure app should run if OmniAuth indicates a failure
+ test do
+ response = get "/auth/failure", {:strategy => 'twitter', :message => 'Things went south!'}, {'rack.session' => {} }
+ assert("status should be 401") { response.status == 401 }
+ assert("text should include 'Can't login'") { response.body.include? "Can't login" }
+ assert("text should include OmniAuth's failure message") { response.body.include? "Things went south!" }
+ end
+ end
+
+ context do
+ teardown { @_rack_mock_sessions = nil; @_rack_test_sessions = nil }
+ setup do
+ $captures = []
+ @app = create_app do |e|
+ e['warden'].authenticate
+ $captures << e['warden'].user
+ Rack::Response.new("DONE").finish
+ end
+ end
+
+ # The callback should also work as a lambda
+ test do
+ session = {}
+ $omni_auth.redirect_after_callback do |env|
+ assert("passed env should have omniauth.auth key") { env.has_key? 'omniauth.auth' }
+ "/path/to/#{env['omniauth.auth']['info']}"
+ end
+
+ response = get("/auth/twitter/callback", {}, {'rack.session' => session, 'omniauth.auth' => {'info' => "alice"}})
+
+ assert("should be redirected") { response.status == 302 }
+ assert("should go to the redirect path"){ response.headers['Location'] == "/path/to/alice" }
+
+ response = get("/path/to/alice", {}, {'rack.session' => session })
+ assert("should have made it into the app") { $captures.size == 1 }
+ assert("should have captured the user"){ $captures.first[:info] == 'alice' }
+ end
end
context do
@@ -83,17 +121,15 @@
# The session scope should store the user
test do
-
session = {}
session[WardenOmniAuth::SCOPE_KEY] = "user"
- expected_redirect = $omni_auth.redirect_after_callback_path
- response = get("/auth/twitter/callback", {}, {'rack.session' => session, 'rack.auth' => {'info' => "fred"}})
+ response = get("/auth/twitter/callback", {}, {'rack.session' => session, 'omniauth.auth' => {'info' => "fred"}})
assert("should be redirected") { response.status == 302 }
- assert("should go to the redirect path"){ response.headers['Location'] == expected_redirect }
+ assert("should go to the redirect path"){ response.headers['Location'] == $expected_redirect }
- response = get(expected_redirect, {}, {'rack.session' => session })
+ response = get($expected_redirect, {}, {'rack.session' => session })
assert("should have made it into the app") { $captures.size == 1 }
assert("should have captured the user"){ $captures.first[:info] == 'fred' }
end
@@ -103,36 +139,35 @@
begin
session = {}
session[WardenOmniAuth::SCOPE_KEY] = "user"
- expected_redirect = $omni_auth.redirect_after_callback_path
- Warden::Strategies[:omni_facebook].on_callback do |user|
+ Warden::Strategies[:omni_facebook].on_callback do |user,strategy|
{:facebook => "user"}
end
- Warden::Strategies[:omni_twitter].on_callback do |user|
+ Warden::Strategies[:omni_twitter].on_callback do |user,strategy|
{:twitter => "user"}
end
- Warden::Strategies[:omni_google_oauth2].on_callback do |user|
+ Warden::Strategies[:omni_google_oauth2].on_callback do |user,strategy|
{:google_oauth2 => "user"}
end
- response = get("/auth/facebook/callback", {}, {'rack.session' => session, 'rack.auth' => {'info' => "fred"}})
- response = get expected_redirect, {}, {'rack.session' => session}
+ response = get("/auth/facebook/callback", {}, {'rack.session' => session, 'omniauth.auth' => {'info' => "fred"}})
+ response = get $expected_redirect, {}, {'rack.session' => session}
assert { $captures.size == 1 }
assert { $captures.first == {:facebook => "user"} }
$captures = []
session = {}
session[WardenOmniAuth::SCOPE_KEY] = "user"
- response = get("/auth/twitter/callback", {}, {'rack.session' => session, 'rack.auth' => {'info' => 'fred'}})
- response = get expected_redirect, {}, {'rack.session' => session}
+ response = get("/auth/twitter/callback", {}, {'rack.session' => session, 'omniauth.auth' => {'info' => 'fred'}})
+ response = get $expected_redirect, {}, {'rack.session' => session}
assert { $captures.size == 1 }
assert { $captures.first == {:twitter => "user"} }
$captures = []
session = {}
session[WardenOmniAuth::SCOPE_KEY] = "user"
- response = get("/auth/google_oauth2/callback", {}, {'rack.session' => session, 'rack.auth' => {'info' => 'fred'}})
- response = get expected_redirect, {}, {'rack.session' => session}
+ response = get("/auth/google_oauth2/callback", {}, {'rack.session' => session, 'omniauth.auth' => {'info' => 'fred'}})
+ response = get $expected_redirect, {}, {'rack.session' => session}
assert { $captures.size == 1 }
assert { $captures.first == {:google_oauth2 => "user"} }
ensure
diff --git a/warden_omniauth.gemspec b/warden_omniauth.gemspec
index 1e83396..52bbeb5 100644
--- a/warden_omniauth.gemspec
+++ b/warden_omniauth.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.rubyforge_project = "warden_omniauth"
s.add_dependency "omniauth"
- s.add_dependency "warden", ">=0.9"
+ s.add_dependency "warden", ">=1.0.0"
s.add_development_dependency "bundler", ">= 1.0.0"