mirror of
https://github.com/heartcombo/devise.git
synced 2026-01-10 08:08:00 -05:00
Merge branch 'omniauth'
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
* after_update_path_for can no longer be defined in ApplicationController
|
||||
|
||||
* enhancements
|
||||
* Added OAuth 2 support
|
||||
* Added OmniAuth support
|
||||
* Added ORM adapter to abstract ORM iteraction
|
||||
* sign_out_via is available in the router to configure the method used for sign out (by github.com/martinrehfeld)
|
||||
* Improved Ajax requests handling in failure app (by github.com/spastorino)
|
||||
* Added request_keys to easily use request specific values (like subdomain) in authentication
|
||||
|
||||
10
Gemfile
10
Gemfile
@@ -2,10 +2,11 @@ source "http://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
gem "rails", "3.0.0"
|
||||
gem "rails"
|
||||
gem "webrat", "0.7.1"
|
||||
gem "mocha", :require => false
|
||||
gem "oauth2"
|
||||
gem "oa-oauth", :require => "omniauth/oauth"
|
||||
gem "oa-openid", :require => "omniauth/openid"
|
||||
|
||||
platforms :jruby do
|
||||
gem 'activerecord-jdbcsqlite3-adapter'
|
||||
@@ -13,9 +14,8 @@ end
|
||||
|
||||
platforms :ruby do
|
||||
gem "sqlite3-ruby"
|
||||
if RUBY_VERSION < '1.9'
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
end
|
||||
gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9'
|
||||
|
||||
group :mongoid do
|
||||
gem "mongo", "1.0.7"
|
||||
gem "mongoid", "2.0.0.beta.18"
|
||||
|
||||
71
Gemfile.lock
71
Gemfile.lock
@@ -32,37 +32,38 @@ GEM
|
||||
activesupport (= 3.0.0)
|
||||
arel (~> 1.0.0)
|
||||
tzinfo (~> 0.3.23)
|
||||
activerecord-jdbc-adapter (0.9.7-java)
|
||||
activerecord-jdbcsqlite3-adapter (0.9.7-java)
|
||||
activerecord-jdbc-adapter (= 0.9.7)
|
||||
jdbc-sqlite3 (>= 3.6.3.054)
|
||||
activerecord-jdbc-adapter (1.0.2-java)
|
||||
activerecord-jdbcsqlite3-adapter (1.0.2-java)
|
||||
activerecord-jdbc-adapter (= 1.0.2)
|
||||
jdbc-sqlite3 (~> 3.6.0)
|
||||
activeresource (3.0.0)
|
||||
activemodel (= 3.0.0)
|
||||
activesupport (= 3.0.0)
|
||||
activesupport (3.0.0)
|
||||
addressable (2.1.2)
|
||||
addressable (2.2.2)
|
||||
arel (1.0.1)
|
||||
activesupport (~> 3.0.0)
|
||||
bcrypt-ruby (2.1.2)
|
||||
bson (1.0.4)
|
||||
bson_ext (1.0.7)
|
||||
builder (2.1.2)
|
||||
columnize (0.3.1)
|
||||
columnize (0.3.2)
|
||||
erubis (2.6.6)
|
||||
abstract (>= 1.0.0)
|
||||
faraday (0.5.0)
|
||||
addressable (~> 2.1.1)
|
||||
faraday (0.5.2)
|
||||
addressable (~> 2.2.2)
|
||||
multipart-post (~> 1.0.1)
|
||||
rack (~> 1.2.1)
|
||||
i18n (0.4.1)
|
||||
jdbc-sqlite3 (3.6.3.054)
|
||||
rack (>= 1.1.0, < 2)
|
||||
i18n (0.4.2)
|
||||
jdbc-sqlite3 (3.6.14.2.056-java)
|
||||
linecache (0.43)
|
||||
mail (2.2.6.1)
|
||||
mail (2.2.9)
|
||||
activesupport (>= 2.3.6)
|
||||
mime-types
|
||||
treetop (>= 1.4.5)
|
||||
i18n (~> 0.4.1)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
mime-types (1.16)
|
||||
mocha (0.9.8)
|
||||
mocha (0.9.9)
|
||||
rake
|
||||
mongo (1.0.7)
|
||||
bson (>= 1.0.4)
|
||||
@@ -72,20 +73,36 @@ GEM
|
||||
mongo (= 1.0.7)
|
||||
tzinfo (~> 0.3.22)
|
||||
will_paginate (~> 3.0.pre)
|
||||
multi_json (0.0.4)
|
||||
multi_json (0.0.5)
|
||||
multipart-post (1.0.1)
|
||||
nokogiri (1.4.3.1)
|
||||
nokogiri (1.4.3.1-java)
|
||||
weakling (>= 0.0.3)
|
||||
oa-core (0.1.6)
|
||||
rack (~> 1.1)
|
||||
oa-oauth (0.1.6)
|
||||
multi_json (~> 0.0.2)
|
||||
nokogiri (~> 1.4.2)
|
||||
oa-core (= 0.1.6)
|
||||
oauth (~> 0.4.0)
|
||||
oauth2 (~> 0.1.0)
|
||||
oa-openid (0.1.6)
|
||||
oa-core (= 0.1.6)
|
||||
rack-openid (~> 1.2.0)
|
||||
ruby-openid-apps-discovery
|
||||
oauth (0.4.4)
|
||||
oauth2 (0.1.0)
|
||||
faraday (~> 0.5.0)
|
||||
multi_json (~> 0.0.4)
|
||||
orm_adapter (0.0.2)
|
||||
orm_adapter (0.0.3)
|
||||
polyglot (0.3.1)
|
||||
rack (1.2.1)
|
||||
rack-mount (0.6.13)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (0.5.5)
|
||||
rack-openid (1.2.0)
|
||||
rack (>= 1.1.0)
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-test (0.5.6)
|
||||
rack (>= 1.0)
|
||||
rails (3.0.0)
|
||||
actionmailer (= 3.0.0)
|
||||
@@ -101,17 +118,20 @@ GEM
|
||||
rake (>= 0.8.4)
|
||||
thor (~> 0.14.0)
|
||||
rake (0.8.7)
|
||||
ruby-debug (0.10.3)
|
||||
ruby-debug (0.10.4)
|
||||
columnize (>= 0.1)
|
||||
ruby-debug-base (~> 0.10.3.0)
|
||||
ruby-debug-base (0.10.3)
|
||||
ruby-debug-base (~> 0.10.4.0)
|
||||
ruby-debug-base (0.10.4)
|
||||
linecache (>= 0.3)
|
||||
ruby-openid (2.1.8)
|
||||
ruby-openid-apps-discovery (1.2.0)
|
||||
ruby-openid (>= 2.1.7)
|
||||
sqlite3-ruby (1.3.1)
|
||||
thor (0.14.1)
|
||||
thor (0.14.4)
|
||||
treetop (1.4.8)
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.23)
|
||||
warden (1.0.0)
|
||||
warden (1.0.1)
|
||||
rack (>= 1.0.0)
|
||||
weakling (0.0.4-java)
|
||||
webrat (0.7.1)
|
||||
@@ -132,9 +152,10 @@ DEPENDENCIES
|
||||
mocha
|
||||
mongo (= 1.0.7)
|
||||
mongoid (= 2.0.0.beta.18)
|
||||
oauth2
|
||||
oa-oauth
|
||||
oa-openid
|
||||
orm_adapter (~> 0.0.2)
|
||||
rails (= 3.0.0)
|
||||
rails
|
||||
ruby-debug (>= 0.10.3)
|
||||
sqlite3-ruby
|
||||
warden (~> 1.0.0)
|
||||
|
||||
@@ -11,7 +11,7 @@ It's composed of 12 modules:
|
||||
|
||||
* Database Authenticatable: encrypts and stores a password in the database to validate the authenticity of an user while signing in. The authentication can be done both through POST requests or HTTP Basic Authentication.
|
||||
* Token Authenticatable: signs in a user based on an authentication token (also known as "single access token"). The token can be given both through query string or HTTP Basic Authentication.
|
||||
* Oauthable: adds OAuth2 support;
|
||||
* Omniauthable: adds Omniauth (github.com/intridea/omniauth) support;
|
||||
* Confirmable: sends emails with confirmation instructions and verifies whether an account is already confirmed during sign in.
|
||||
* Recoverable: resets the user password and sends reset instructions.
|
||||
* Registerable: handles signing up users through a registration process, also allowing them to edit and destroy their account.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
class Devise::OauthCallbacksController < ApplicationController
|
||||
include Devise::Controllers::InternalHelpers
|
||||
include Devise::Oauth::InternalHelpers
|
||||
end
|
||||
26
app/controllers/devise/omniauth_callbacks_controller.rb
Normal file
26
app/controllers/devise/omniauth_callbacks_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
class Devise::OmniauthCallbacksController < ApplicationController
|
||||
include Devise::Controllers::InternalHelpers
|
||||
|
||||
def failure
|
||||
set_flash_message :alert, :failure, :kind => failed_strategy.name.to_s.humanize, :reason => failure_message
|
||||
redirect_to after_omniauth_failure_path_for(resource_name)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def failed_strategy
|
||||
env["omniauth.failed_strategy"]
|
||||
end
|
||||
|
||||
def failure_message
|
||||
exception = env["omniauth.error"]
|
||||
error = exception.error_reason if exception.respond_to?(:error_reason)
|
||||
error ||= exception.error if exception.respond_to?(:error)
|
||||
error ||= env["omniauth.failure_key"]
|
||||
error.to_s.humanize if error
|
||||
end
|
||||
|
||||
def after_omniauth_failure_path_for(scope)
|
||||
new_session_path(scope)
|
||||
end
|
||||
end
|
||||
@@ -19,6 +19,7 @@ class Devise::RegistrationsController < ApplicationController
|
||||
sign_in_and_redirect(resource_name, resource)
|
||||
else
|
||||
set_flash_message :notice, :inactive_signed_up, :reason => resource.inactive_message.to_s
|
||||
expire_session_data_after_sign_in!
|
||||
redirect_to after_inactive_sign_up_path_for(resource)
|
||||
end
|
||||
else
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.oauthable? %>
|
||||
<%- resource_class.oauth_providers.each do |provider| %>
|
||||
<%= link_to "Sign in with #{provider.to_s.titleize}", oauth_authorize_url(resource_name, provider) %><br />
|
||||
<%- if devise_mapping.omniauthable? %>
|
||||
<%- resource_class.omniauth_providers.each do |provider| %>
|
||||
<%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %><br />
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
@@ -34,7 +34,7 @@ en:
|
||||
unlocks:
|
||||
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
|
||||
unlocked: 'Your account was successfully unlocked. You are now signed in.'
|
||||
oauth_callbacks:
|
||||
omniauth_callbacks:
|
||||
success: 'Successfully authorized from %{kind} account.'
|
||||
failure: 'Could not authorize you from %{kind} because "%{reason}".'
|
||||
mailer:
|
||||
|
||||
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["Jos\303\251 Valim", "Carlos Ant\303\264nio"]
|
||||
s.date = %q{2010-10-10}
|
||||
s.date = %q{2010-10-15}
|
||||
s.description = %q{Flexible authentication solution for Rails with Warden}
|
||||
s.email = %q{contact@plataformatec.com.br}
|
||||
s.extra_rdoc_files = [
|
||||
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
|
||||
"MIT-LICENSE",
|
||||
"README.rdoc",
|
||||
"app/controllers/devise/confirmations_controller.rb",
|
||||
"app/controllers/devise/oauth_callbacks_controller.rb",
|
||||
"app/controllers/devise/omniauth_callbacks_controller.rb",
|
||||
"app/controllers/devise/passwords_controller.rb",
|
||||
"app/controllers/devise/registrations_controller.rb",
|
||||
"app/controllers/devise/sessions_controller.rb",
|
||||
@@ -65,7 +65,7 @@ Gem::Specification.new do |s|
|
||||
"lib/devise/models/database_authenticatable.rb",
|
||||
"lib/devise/models/encryptable.rb",
|
||||
"lib/devise/models/lockable.rb",
|
||||
"lib/devise/models/oauthable.rb",
|
||||
"lib/devise/models/omniauthable.rb",
|
||||
"lib/devise/models/recoverable.rb",
|
||||
"lib/devise/models/registerable.rb",
|
||||
"lib/devise/models/rememberable.rb",
|
||||
@@ -74,12 +74,10 @@ Gem::Specification.new do |s|
|
||||
"lib/devise/models/trackable.rb",
|
||||
"lib/devise/models/validatable.rb",
|
||||
"lib/devise/modules.rb",
|
||||
"lib/devise/oauth.rb",
|
||||
"lib/devise/oauth/config.rb",
|
||||
"lib/devise/oauth/helpers.rb",
|
||||
"lib/devise/oauth/internal_helpers.rb",
|
||||
"lib/devise/oauth/test_helpers.rb",
|
||||
"lib/devise/oauth/url_helpers.rb",
|
||||
"lib/devise/omniauth.rb",
|
||||
"lib/devise/omniauth/config.rb",
|
||||
"lib/devise/omniauth/test_helpers.rb",
|
||||
"lib/devise/omniauth/url_helpers.rb",
|
||||
"lib/devise/orm/active_record.rb",
|
||||
"lib/devise/orm/mongoid.rb",
|
||||
"lib/devise/path_checker.rb",
|
||||
@@ -121,7 +119,7 @@ Gem::Specification.new do |s|
|
||||
"test/integration/database_authenticatable_test.rb",
|
||||
"test/integration/http_authenticatable_test.rb",
|
||||
"test/integration/lockable_test.rb",
|
||||
"test/integration/oauthable_test.rb",
|
||||
"test/integration/omniauthable_test.rb",
|
||||
"test/integration/recoverable_test.rb",
|
||||
"test/integration/registerable_test.rb",
|
||||
"test/integration/rememberable_test.rb",
|
||||
@@ -136,7 +134,6 @@ Gem::Specification.new do |s|
|
||||
"test/models/database_authenticatable_test.rb",
|
||||
"test/models/encryptable_test.rb",
|
||||
"test/models/lockable_test.rb",
|
||||
"test/models/oauthable_test.rb",
|
||||
"test/models/recoverable_test.rb",
|
||||
"test/models/rememberable_test.rb",
|
||||
"test/models/timeoutable_test.rb",
|
||||
@@ -144,8 +141,7 @@ Gem::Specification.new do |s|
|
||||
"test/models/trackable_test.rb",
|
||||
"test/models/validatable_test.rb",
|
||||
"test/models_test.rb",
|
||||
"test/oauth/config_test.rb",
|
||||
"test/oauth/url_helpers_test.rb",
|
||||
"test/omniauth/url_helpers_test.rb",
|
||||
"test/orm/active_record.rb",
|
||||
"test/orm/mongoid.rb",
|
||||
"test/rails_app/app/active_record/admin.rb",
|
||||
@@ -157,6 +153,7 @@ Gem::Specification.new do |s|
|
||||
"test/rails_app/app/controllers/home_controller.rb",
|
||||
"test/rails_app/app/controllers/publisher/registrations_controller.rb",
|
||||
"test/rails_app/app/controllers/publisher/sessions_controller.rb",
|
||||
"test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb",
|
||||
"test/rails_app/app/controllers/users_controller.rb",
|
||||
"test/rails_app/app/helpers/application_helper.rb",
|
||||
"test/rails_app/app/mongoid/admin.rb",
|
||||
|
||||
@@ -5,7 +5,7 @@ require 'set'
|
||||
|
||||
module Devise
|
||||
autoload :FailureApp, 'devise/failure_app'
|
||||
autoload :Oauth, 'devise/oauth'
|
||||
autoload :OmniAuth, 'devise/omniauth'
|
||||
autoload :PathChecker, 'devise/path_checker'
|
||||
autoload :Schema, 'devise/schema'
|
||||
autoload :TestHelpers, 'devise/test_helpers'
|
||||
@@ -170,7 +170,7 @@ module Devise
|
||||
|
||||
# Which formats should be treated as navigational.
|
||||
mattr_accessor :navigational_formats
|
||||
@@navigational_formats = [:html]
|
||||
@@navigational_formats = [:"*/*", :html]
|
||||
|
||||
# When set to true, signing out an user signs out all other scopes.
|
||||
mattr_accessor :sign_out_all_scopes
|
||||
@@ -180,29 +180,21 @@ module Devise
|
||||
mattr_accessor :sign_out_via
|
||||
@@sign_out_via = :get
|
||||
|
||||
# Oauth providers
|
||||
mattr_accessor :oauth_providers
|
||||
@@oauth_providers = []
|
||||
|
||||
# PRIVATE CONFIGURATION
|
||||
|
||||
# Store scopes mappings.
|
||||
mattr_reader :mappings
|
||||
@@mappings = ActiveSupport::OrderedHash.new
|
||||
|
||||
# Oauth configurations.
|
||||
mattr_reader :oauth_configs
|
||||
@@oauth_configs = ActiveSupport::OrderedHash.new
|
||||
# Omniauth configurations.
|
||||
mattr_reader :omniauth_configs
|
||||
@@omniauth_configs = ActiveSupport::OrderedHash.new
|
||||
|
||||
# Define a set of modules that are called when a mapping is added.
|
||||
mattr_reader :helpers
|
||||
@@helpers = Set.new
|
||||
@@helpers << Devise::Controllers::Helpers
|
||||
|
||||
# Define a set of modules that are called when a provider is added.
|
||||
mattr_reader :oauth_helpers
|
||||
@@oauth_helpers = Set.new
|
||||
|
||||
# Private methods to interface with Warden.
|
||||
mattr_accessor :warden_config
|
||||
@@warden_config = nil
|
||||
@@ -214,6 +206,10 @@ module Devise
|
||||
yield self
|
||||
end
|
||||
|
||||
def self.omniauth_providers
|
||||
omniauth_configs.keys
|
||||
end
|
||||
|
||||
def self.cookie_domain=(value)
|
||||
ActiveSupport::Deprecation.warn "Devise.cookie_domain=(value) is deprecated. "
|
||||
"Please use Devise.cookie_options = { :domain => value } instead."
|
||||
@@ -312,29 +308,19 @@ module Devise
|
||||
@@warden_config_block = block
|
||||
end
|
||||
|
||||
# Specify an oauth provider.
|
||||
# Specify an omniauth provider.
|
||||
#
|
||||
# config.oauth :github, APP_ID, APP_SECRET,
|
||||
# :site => 'https://github.com/',
|
||||
# :authorize_path => '/login/oauth/authorize',
|
||||
# :access_token_path => '/login/oauth/access_token',
|
||||
# :scope => %w(user public_repo)
|
||||
# config.omniauth :github, APP_ID, APP_SECRET
|
||||
#
|
||||
def self.oauth(provider, *args)
|
||||
@@helpers << Devise::Oauth::UrlHelpers
|
||||
@@oauth_helpers << Devise::Oauth::InternalHelpers
|
||||
|
||||
@@oauth_providers << provider
|
||||
@@oauth_providers.uniq!
|
||||
|
||||
@@oauth_helpers.each { |h| h.define_oauth_helpers(provider) }
|
||||
@@oauth_configs[provider] = Devise::Oauth::Config.new(*args)
|
||||
def self.omniauth(provider, *args)
|
||||
@@helpers << Devise::OmniAuth::UrlHelpers
|
||||
@@omniauth_configs[provider] = Devise::OmniAuth::Config.new(provider, args)
|
||||
end
|
||||
|
||||
# Include helpers in the given scope to AC and AV.
|
||||
def self.include_helpers(scope)
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
include scope::Helpers
|
||||
include scope::Helpers if defined?(scope::Helpers)
|
||||
include scope::UrlHelpers
|
||||
end
|
||||
|
||||
|
||||
@@ -104,10 +104,11 @@ module Devise
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
resource = args.last || resource_or_scope
|
||||
|
||||
expire_session_data_after_sign_in!
|
||||
|
||||
if options[:bypass]
|
||||
warden.session_serializer.store(resource, scope)
|
||||
else
|
||||
expire_session_data_after_sign_in!
|
||||
warden.set_user(resource, options.merge!(:scope => scope))
|
||||
end
|
||||
end
|
||||
@@ -195,7 +196,13 @@ module Devise
|
||||
options = args.extract_options!
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
resource = args.last || resource_or_scope
|
||||
sign_in(scope, resource, options) unless warden.user(scope) == resource
|
||||
|
||||
if warden.user(scope) == resource
|
||||
expire_session_data_after_sign_in!
|
||||
else
|
||||
sign_in(scope, resource, options)
|
||||
end
|
||||
|
||||
redirect_for_sign_in(scope, resource)
|
||||
end
|
||||
|
||||
@@ -219,9 +226,10 @@ module Devise
|
||||
redirect_to after_sign_out_path_for(scope)
|
||||
end
|
||||
|
||||
# A hook called to expire session data after sign up/in. This is used
|
||||
# by a few extensions, like oauth, to expire tokens stored in session.
|
||||
# A hook called to expire session data after sign up/in. All keys
|
||||
# stored under "devise." namespace are removed after sign in.
|
||||
def expire_session_data_after_sign_in!
|
||||
session.keys.grep(/^devise\./).each { |k| session.delete(k) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,6 +40,11 @@ module Devise
|
||||
raise "Could not find a valid mapping for #{duck}"
|
||||
end
|
||||
|
||||
def self.find_by_path!(path, path_type=:fullpath)
|
||||
Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
|
||||
raise "Could not find a valid mapping for path #{path}"
|
||||
end
|
||||
|
||||
def initialize(name, options) #:nodoc:
|
||||
@plural = (options[:as] ? "#{options[:as]}_#{name}" : name).to_sym
|
||||
@singular = (options[:singular] || @plural.to_s.singularize).to_sym
|
||||
@@ -84,7 +89,7 @@ module Devise
|
||||
end
|
||||
|
||||
def fullpath
|
||||
"#{@path_prefix}/#{@path}".squeeze("/")
|
||||
"/#{@path_prefix}/#{@path}".squeeze("/")
|
||||
end
|
||||
|
||||
# Create magic predicates for verifying what module is activated by this map.
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
module Devise
|
||||
module Models
|
||||
# Adds OAuth support to your model. The whole workflow is deeply discussed in the
|
||||
# README. This module adds just a class +oauth_access_token+ helper to your model
|
||||
# which assists you on creating an access token. All the other OAuth hooks in
|
||||
# Devise must be implemented by yourself in your application.
|
||||
#
|
||||
# == Options
|
||||
#
|
||||
# Oauthable adds the following options to devise_for:
|
||||
#
|
||||
# * +oauth_providers+: Which providers are avaialble to this model. It expects an array:
|
||||
#
|
||||
# devise_for :database_authenticatable, :oauthable, :oauth_providers => [:twitter]
|
||||
#
|
||||
module Oauthable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def oauth_configs #:nodoc:
|
||||
Devise.oauth_configs.slice(*oauth_providers)
|
||||
end
|
||||
|
||||
# Pass a token stored in the database to this object to get an OAuth2::AccessToken
|
||||
# object back, as the one received in your model hook.
|
||||
#
|
||||
# For each provider you add, you may want to add a hook to retrieve the token based
|
||||
# on the column you stored the token in the database. For example, you may want to
|
||||
# the following for twitter:
|
||||
#
|
||||
# def oauth_twitter_token
|
||||
# @oauth_twitter_token ||= self.class.oauth_access_token(:twitter, twitter_token)
|
||||
# end
|
||||
#
|
||||
# You can call get, post, put and delete in this object to access Twitter's API.
|
||||
def oauth_access_token(provider, token)
|
||||
oauth_configs[provider].access_token_by_token(token)
|
||||
end
|
||||
|
||||
# TODO Implement this method in the future.
|
||||
# def refresh_oauth_token(provider, refresh_token)
|
||||
# returns access_token
|
||||
# end
|
||||
|
||||
Devise::Models.config(self, :oauth_providers)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
23
lib/devise/models/omniauthable.rb
Normal file
23
lib/devise/models/omniauthable.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
require 'devise/omniauth'
|
||||
|
||||
module Devise
|
||||
module Models
|
||||
# Adds OmniAuth support to your model.
|
||||
#
|
||||
# == Options
|
||||
#
|
||||
# Oauthable adds the following options to devise_for:
|
||||
#
|
||||
# * +omniauth_providers+: Which providers are avaialble to this model. It expects an array:
|
||||
#
|
||||
# devise_for :database_authenticatable, :omniauthable, :omniauth_providers => [:twitter]
|
||||
#
|
||||
module Omniauthable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
Devise::Models.config(self, :omniauth_providers)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -11,7 +11,7 @@ Devise.with_options :model => true do |d|
|
||||
|
||||
# Other authentications
|
||||
d.add_module :encryptable
|
||||
d.add_module :oauthable, :controller => :oauth_callbacks, :route => :oauth_callback
|
||||
d.add_module :omniauthable, :controller => :omniauth_callbacks, :route => :omniauth_callback
|
||||
|
||||
# Misc after
|
||||
routes = [nil, :new, :edit]
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
begin
|
||||
require "oauth2"
|
||||
rescue LoadError => e
|
||||
warn "Could not load 'oauth2'. Please ensure you have the gem installed and listed in your Gemfile."
|
||||
raise
|
||||
end
|
||||
|
||||
module Devise
|
||||
module Oauth
|
||||
autoload :Config, "devise/oauth/config"
|
||||
autoload :Helpers, "devise/oauth/helpers"
|
||||
autoload :InternalHelpers, "devise/oauth/internal_helpers"
|
||||
autoload :UrlHelpers, "devise/oauth/url_helpers"
|
||||
autoload :TestHelpers, "devise/oauth/test_helpers"
|
||||
|
||||
class << self
|
||||
delegate :short_circuit_authorizers!, :unshort_circuit_authorizers!, :to => "Devise::Oauth::TestHelpers"
|
||||
|
||||
def test_mode!
|
||||
Faraday.default_adapter = :test
|
||||
ActiveSupport.on_load(:action_controller) { include Devise::Oauth::TestHelpers }
|
||||
ActiveSupport.on_load(:action_view) { include Devise::Oauth::TestHelpers }
|
||||
end
|
||||
|
||||
def stub!(provider, stubs=nil, &block)
|
||||
raise "You either need to pass stubs as a block or as a parameter" unless block_given? || stubs
|
||||
stubs ||= Faraday::Adapter::Test::Stubs.new(&block)
|
||||
Devise.oauth_configs[provider].build_connection do |b|
|
||||
b.adapter :test, stubs
|
||||
end
|
||||
end
|
||||
|
||||
def reset_stubs!(*providers)
|
||||
target = providers.any? ? Devise.oauth_configs.slice(*providers) : Devise.oauth_configs
|
||||
target.each_value do |v|
|
||||
v.build_connection { |b| b.adapter Faraday.default_adapter }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,33 +0,0 @@
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
|
||||
module Devise
|
||||
module Oauth
|
||||
# A configuration object that holds the OAuth2::Client object
|
||||
# and all configuration values given config.oauth.
|
||||
class Config
|
||||
attr_reader :scope, :client
|
||||
|
||||
def initialize(app_id, app_secret, options)
|
||||
@scope = Array.wrap(options.delete(:scope))
|
||||
@client = OAuth2::Client.new(app_id, app_secret, options)
|
||||
end
|
||||
|
||||
def authorize_url(options)
|
||||
options[:scope] ||= @scope.join(',')
|
||||
client.web_server.authorize_url(options)
|
||||
end
|
||||
|
||||
def access_token_by_code(code, redirect_uri=nil)
|
||||
client.web_server.get_access_token(code, :redirect_uri => redirect_uri)
|
||||
end
|
||||
|
||||
def access_token_by_token(token)
|
||||
OAuth2::AccessToken.new(client, token)
|
||||
end
|
||||
|
||||
def build_connection(&block)
|
||||
client.connection.build(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,18 +0,0 @@
|
||||
module Devise
|
||||
module Oauth
|
||||
# Provides a few helpers that are included in ActionController::Base
|
||||
# for convenience.
|
||||
module Helpers
|
||||
|
||||
protected
|
||||
|
||||
# Overwrite expire_session_data_after_sign_in! so it removes all
|
||||
# oauth tokens from session ensuring registrations done in a row
|
||||
# do not try to store the same token in the database.
|
||||
def expire_session_data_after_sign_in!
|
||||
super
|
||||
session.keys.grep(/_oauth_token$/).each { |k| session.delete(k) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,182 +0,0 @@
|
||||
module Devise
|
||||
module Oauth
|
||||
module InternalHelpers
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def self.define_oauth_helpers(name) #:nodoc:
|
||||
alias_method(name, :callback_action)
|
||||
public name
|
||||
end
|
||||
|
||||
included do
|
||||
helpers = %w(oauth_callback oauth_provider oauth_config)
|
||||
hide_action *helpers
|
||||
helper_method *helpers
|
||||
before_filter :valid_oauth_callback?, :oauth_error_happened?
|
||||
end
|
||||
|
||||
# Returns the oauth_callback (also aliased as oauth_provider) as a symbol.
|
||||
# For example: :github.
|
||||
def oauth_callback
|
||||
@oauth_callback ||= action_name.to_sym
|
||||
end
|
||||
alias :oauth_provider :oauth_callback
|
||||
|
||||
# Returns the configuration object for this oauth callback.
|
||||
def oauth_config
|
||||
@oauth_client ||= resource_class.oauth_configs[oauth_callback]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This method checks three things:
|
||||
#
|
||||
# * If the URL being accessed is a valid provider for the given scope;
|
||||
# * If code or error was streamed back from the server;
|
||||
# * If the resource class implements the required hook;
|
||||
#
|
||||
def valid_oauth_callback?
|
||||
unless oauth_config
|
||||
unknown_action! "Skipping #{oauth_callback} OAuth because configuration " <<
|
||||
"could not be found for model #{resource_name}."
|
||||
end
|
||||
|
||||
unless params[:code] || params[:error] || params[:error_reason]
|
||||
unknown_action! "Skipping #{oauth_callback} OAuth because code nor error were sent."
|
||||
end
|
||||
|
||||
unless resource_class.respond_to?(oauth_model_callback)
|
||||
raise "#{resource_class.name} does not respond to #{oauth_model_callback}. " <<
|
||||
"Check the OAuth section in the README for more information."
|
||||
end
|
||||
end
|
||||
|
||||
# Check if an error was sent by the authorizer. If it happened, we redirect
|
||||
# to url specified by after_oauth_failure_path_for, which defaults to new_session_path.
|
||||
#
|
||||
# By default, Devise shows a custom message from I18n saying the user could
|
||||
# not be authenticated and the reason:
|
||||
#
|
||||
# en:
|
||||
# devise:
|
||||
# oauth_callbacks:
|
||||
# failure: 'Could not authorize you from %{kind} because "%{reason}".'
|
||||
#
|
||||
# Let's suppose the reason returned by a Github was "access_denied". It will show:
|
||||
#
|
||||
# Could not authorize you from Github because "Access denied"
|
||||
#
|
||||
# And it will also be logged on console:
|
||||
#
|
||||
# github oauth failed: "access_denied".
|
||||
#
|
||||
# However, each specific error message can be customized using I18n:
|
||||
#
|
||||
# en:
|
||||
# devise:
|
||||
# oauth_callbacks:
|
||||
# access_denied: 'You did not give access to our application on %{kind}.'
|
||||
#
|
||||
# Note "access_denied" follows the same lookup rule described in set_oauth_flash_message
|
||||
# method. Besides, is important to remember most errors are specified by OAuth 2
|
||||
# specification. But a few providers do not use them yet.
|
||||
#
|
||||
# TODO: Currently, Facebook is returning error_reason=user_denied when
|
||||
# the user denies, but the specification defines error=access_denied instead.
|
||||
def oauth_error_happened?
|
||||
if error = params[:error] || params[:error_reason]
|
||||
# Some providers returns access-denied instead of access_denied.
|
||||
error = error.to_s.gsub("-", "_")
|
||||
logger.warn "[Devise] #{oauth_callback} oauth failed: #{error.inspect}."
|
||||
|
||||
set_oauth_flash_message :alert, :failure, :reason => error.humanize
|
||||
redirect_to after_oauth_failure_path_for(resource_name)
|
||||
end
|
||||
end
|
||||
|
||||
# The model method used as hook.
|
||||
def oauth_model_callback #:nodoc:
|
||||
"find_for_#{oauth_callback}_oauth"
|
||||
end
|
||||
|
||||
# The session key to store the token.
|
||||
def oauth_session_key #:nodoc:
|
||||
"#{resource_name}_#{oauth_callback}_oauth_token"
|
||||
end
|
||||
|
||||
# The callback redirect uri. Used to request the access token.
|
||||
def oauth_redirect_uri #:nodoc:
|
||||
oauth_callback_url(resource_name, oauth_callback)
|
||||
end
|
||||
|
||||
# This is the implementation for all OAuth actions.
|
||||
def callback_action
|
||||
access_token = oauth_config.access_token_by_code(params[:code], oauth_redirect_uri)
|
||||
self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource)
|
||||
|
||||
if resource.persisted? && resource.errors.empty?
|
||||
set_oauth_flash_message :notice, :success
|
||||
sign_in_and_redirect resource_name, resource, :event => :authentication
|
||||
else
|
||||
session[oauth_session_key] = access_token.token
|
||||
clean_up_passwords(resource)
|
||||
render_for_oauth
|
||||
end
|
||||
end
|
||||
|
||||
# Handles oauth flash messages by adding a cascade. The default messages
|
||||
# are always in the controller namespace:
|
||||
#
|
||||
# en:
|
||||
# devise:
|
||||
# oauth_callbacks:
|
||||
# success: 'Successfully authorized from %{kind} account.'
|
||||
# failure: 'Could not authorize you from %{kind} because "%{reason}".'
|
||||
# skipped: 'Skipped Oauth authorization for %{kind}.'
|
||||
#
|
||||
# But they can also be nested according to the oauth provider:
|
||||
#
|
||||
# en:
|
||||
# devise:
|
||||
# oauth_callbacks:
|
||||
# github:
|
||||
# success: 'Hello coder! Welcome to our app!'
|
||||
#
|
||||
# And finally by Devise scope:
|
||||
#
|
||||
# en:
|
||||
# devise:
|
||||
# oauth_callbacks:
|
||||
# admin:
|
||||
# github:
|
||||
# success: 'Hello coder with high permissions! Can I get a raise?'
|
||||
#
|
||||
def set_oauth_flash_message(key, type, options={})
|
||||
options[:kind] = oauth_callback.to_s.titleize
|
||||
options[:default] = type.to_sym
|
||||
set_flash_message(key, "#{oauth_callback}.#{type}", options)
|
||||
end
|
||||
|
||||
# Choose which template to render when a not persisted resource is
|
||||
# returned in the find_for_x_oauth. By default, it renders registrations/new.
|
||||
def render_for_oauth
|
||||
render_with_scope :new, devise_mapping.controllers[:registrations]
|
||||
end
|
||||
|
||||
# The default hook used by oauth to specify the redirect url for success.
|
||||
def after_oauth_success_path_for(resource)
|
||||
after_sign_in_path_for(resource)
|
||||
end
|
||||
|
||||
# The default hook used by oauth to specify the redirect url for failure.
|
||||
def after_oauth_failure_path_for(scope)
|
||||
new_session_path(scope)
|
||||
end
|
||||
|
||||
# Overwrite redirect_for_sign_in so it takes uses after_oauth_success_path_for.
|
||||
def redirect_for_sign_in(scope, resource) #:nodoc:
|
||||
redirect_to stored_location_for(scope) || after_oauth_success_path_for(resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
module Devise
|
||||
module Oauth
|
||||
module TestHelpers #:nodoc:
|
||||
def self.short_circuit_authorizers!
|
||||
module_eval <<-ALIASES, __FILE__, __LINE__ + 1
|
||||
def oauth_authorize_url(scope, provider)
|
||||
oauth_callback_path(scope, provider, :code => "12345")
|
||||
end
|
||||
ALIASES
|
||||
|
||||
Devise.mappings.each_value do |m|
|
||||
next unless m.oauthable?
|
||||
|
||||
module_eval <<-ALIASES, __FILE__, __LINE__ + 1
|
||||
def #{m.name}_oauth_authorize_url(provider)
|
||||
#{m.name}_oauth_callback_path(provider, :code => "12345")
|
||||
end
|
||||
ALIASES
|
||||
end
|
||||
end
|
||||
|
||||
def self.unshort_circuit_authorizers!
|
||||
module_eval do
|
||||
instance_methods.each { |m| remove_method(m) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,35 +0,0 @@
|
||||
module Devise
|
||||
module Oauth
|
||||
module UrlHelpers
|
||||
def self.define_helpers(mapping)
|
||||
return unless mapping.oauthable?
|
||||
|
||||
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
|
||||
def #{mapping.name}_oauth_authorize_url(provider, options={})
|
||||
if config = Devise.oauth_configs[provider.to_sym]
|
||||
options[:redirect_uri] ||= #{mapping.name}_oauth_callback_url(provider.to_s)
|
||||
config.authorize_url(options)
|
||||
else
|
||||
raise ArgumentError, "Could not find oauth provider \#{provider.inspect}"
|
||||
end
|
||||
end
|
||||
URL_HELPERS
|
||||
end
|
||||
|
||||
def oauth_authorize_url(resource_or_scope, *args)
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
send("#{scope}_oauth_authorize_url", *args)
|
||||
end
|
||||
|
||||
def oauth_callback_url(resource_or_scope, *args)
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
send("#{scope}_oauth_callback_url", *args)
|
||||
end
|
||||
|
||||
def oauth_callback_path(resource_or_scope, *args)
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
send("#{scope}_oauth_callback_path", *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
47
lib/devise/omniauth.rb
Normal file
47
lib/devise/omniauth.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
begin
|
||||
require "omniauth/core"
|
||||
rescue LoadError => e
|
||||
warn "Could not load 'omniauth/core'. Please ensure you have the oa-core gem installed and listed in your Gemfile."
|
||||
raise
|
||||
end
|
||||
|
||||
module OmniAuth
|
||||
# TODO HAXES Backport to OmniAuth
|
||||
module Strategy #:nodoc:
|
||||
def initialize(app, name, *args)
|
||||
@app = app
|
||||
@name = name.to_sym
|
||||
@options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def fail!(message_key, exception = nil)
|
||||
self.env['omniauth.error'] = exception
|
||||
self.env['omniauth.failure_key'] = message_key
|
||||
self.env['omniauth.failed_strategy'] = self
|
||||
OmniAuth.config.on_failure.call(self.env, message_key.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Clean up the default path_prefix. It will be automatically set by Devise.
|
||||
OmniAuth.config.path_prefix = nil
|
||||
|
||||
OmniAuth.config.on_failure = Proc.new do |env, key|
|
||||
env['devise.mapping'] = Devise::Mapping.find_by_path!(env['PATH_INFO'], :path)
|
||||
controller_klass = "#{env['devise.mapping'].controllers[:omniauth_callbacks].camelize}Controller"
|
||||
controller_klass.constantize.action(:failure).call(env)
|
||||
end
|
||||
|
||||
module Devise
|
||||
module OmniAuth
|
||||
autoload :Config, "devise/omniauth/config"
|
||||
autoload :UrlHelpers, "devise/omniauth/url_helpers"
|
||||
autoload :TestHelpers, "devise/omniauth/test_helpers"
|
||||
|
||||
class << self
|
||||
delegate :short_circuit_authorizers!, :unshort_circuit_authorizers!,
|
||||
:test_mode!, :stub!, :reset_stubs!, :to => "Devise::OmniAuth::TestHelpers"
|
||||
end
|
||||
end
|
||||
end
|
||||
30
lib/devise/omniauth/config.rb
Normal file
30
lib/devise/omniauth/config.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
module Devise
|
||||
module OmniAuth
|
||||
class Config
|
||||
attr_accessor :strategy
|
||||
attr_reader :args
|
||||
|
||||
def initialize(provider, args)
|
||||
@provider = provider
|
||||
@args = args
|
||||
@strategy = nil
|
||||
end
|
||||
|
||||
def strategy_class
|
||||
::OmniAuth::Strategies.const_get("#{::OmniAuth::Utils.camelize(@provider.to_s)}")
|
||||
end
|
||||
|
||||
def check_if_allow_stubs!
|
||||
raise "OmniAuth strategy for #{@provider} does not allow stubs, only OAuth2 ones do." unless allow_stubs?
|
||||
end
|
||||
|
||||
def allow_stubs?
|
||||
defined?(::OmniAuth::Strategies::OAuth2) && strategy.is_a?(::OmniAuth::Strategies::OAuth2)
|
||||
end
|
||||
|
||||
def build_connection(&block)
|
||||
strategy.client.connection.build(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
57
lib/devise/omniauth/test_helpers.rb
Normal file
57
lib/devise/omniauth/test_helpers.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
module Devise
|
||||
module OmniAuth
|
||||
module TestHelpers
|
||||
def self.test_mode!
|
||||
Faraday.default_adapter = :test if defined?(Faraday)
|
||||
ActiveSupport.on_load(:action_controller) { include Devise::OmniAuth::TestHelpers }
|
||||
ActiveSupport.on_load(:action_view) { include Devise::OmniAuth::TestHelpers }
|
||||
end
|
||||
|
||||
def self.stub!(provider, stubs=nil, &block)
|
||||
raise "You either need to pass stubs as a block or as a parameter" unless block_given? || stubs
|
||||
|
||||
config = Devise.omniauth_configs[provider]
|
||||
raise "Could not find configuration for #{provider.to_s} omniauth provider" unless config
|
||||
|
||||
config.check_if_allow_stubs!
|
||||
stubs ||= Faraday::Adapter::Test::Stubs.new(&block)
|
||||
|
||||
config.build_connection do |b|
|
||||
b.adapter :test, stubs
|
||||
end
|
||||
end
|
||||
|
||||
def self.reset_stubs!(*providers)
|
||||
target = providers.any? ? Devise.omniauth_configs.slice(*providers) : Devise.omniauth_configs
|
||||
target.each_value do |config|
|
||||
next unless config.allow_stubs?
|
||||
config.build_connection { |b| b.adapter Faraday.default_adapter }
|
||||
end
|
||||
end
|
||||
|
||||
def self.short_circuit_authorizers!
|
||||
module_eval <<-ALIASES, __FILE__, __LINE__ + 1
|
||||
def omniauth_authorize_path(*args)
|
||||
omniauth_callback_path(*args)
|
||||
end
|
||||
ALIASES
|
||||
|
||||
Devise.mappings.each_value do |m|
|
||||
next unless m.omniauthable?
|
||||
|
||||
module_eval <<-ALIASES, __FILE__, __LINE__ + 1
|
||||
def #{m.name}_omniauth_authorize_path(provider)
|
||||
#{m.name}_omniauth_callback_path(provider)
|
||||
end
|
||||
ALIASES
|
||||
end
|
||||
end
|
||||
|
||||
def self.unshort_circuit_authorizers!
|
||||
module_eval do
|
||||
instance_methods.each { |m| remove_method(m) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
29
lib/devise/omniauth/url_helpers.rb
Normal file
29
lib/devise/omniauth/url_helpers.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
module Devise
|
||||
module OmniAuth
|
||||
module UrlHelpers
|
||||
def self.define_helpers(mapping)
|
||||
return unless mapping.omniauthable?
|
||||
|
||||
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
|
||||
def #{mapping.name}_omniauth_authorize_path(provider, params = {})
|
||||
if Devise.omniauth_configs[provider.to_sym]
|
||||
"/#{mapping.path}/auth/\#{provider}\#{'?'+params.to_param if params.present?}"
|
||||
else
|
||||
raise ArgumentError, "Could not find omniauth provider \#{provider.inspect}"
|
||||
end
|
||||
end
|
||||
URL_HELPERS
|
||||
end
|
||||
|
||||
def omniauth_authorize_path(resource_or_scope, *args)
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
send("#{scope}_omniauth_authorize_path", *args)
|
||||
end
|
||||
|
||||
def omniauth_callback_path(resource_or_scope, *args)
|
||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||
send("#{scope}_omniauth_callback_path", *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,12 +5,6 @@ module Devise
|
||||
class Engine < ::Rails::Engine
|
||||
config.devise = Devise
|
||||
|
||||
# Skip eager load of controllers because it is handled by Devise
|
||||
# to avoid loading unused controllers.
|
||||
target = paths.is_a?(Hash) ? paths["app/controllers"] : paths.app.controllers
|
||||
target.autoload!
|
||||
target.skip_eager_load!
|
||||
|
||||
# Initialize Warden and copy its configurations.
|
||||
config.app_middleware.use Warden::Manager do |config|
|
||||
Devise.warden_config = config
|
||||
@@ -19,18 +13,19 @@ module Devise
|
||||
# Force routes to be loaded if we are doing any eager load.
|
||||
config.before_eager_load { |app| app.reload_routes! }
|
||||
|
||||
initializer "devise.add_filters" do |app|
|
||||
app.config.filter_parameters += [:password, :password_confirmation]
|
||||
app.config.filter_parameters.uniq
|
||||
end
|
||||
|
||||
initializer "devise.url_helpers" do
|
||||
Devise.include_helpers(Devise::Controllers)
|
||||
end
|
||||
|
||||
initializer "devise.oauth_url_helpers" do
|
||||
if Devise.oauth_providers.any?
|
||||
Devise.include_helpers(Devise::Oauth)
|
||||
initializer "devise.omniauth" do |app|
|
||||
Devise.omniauth_configs.each do |provider, config|
|
||||
app.middleware.use config.strategy_class, *config.args do |strategy|
|
||||
config.strategy = strategy
|
||||
end
|
||||
end
|
||||
|
||||
if Devise.omniauth_configs.any?
|
||||
Devise.include_helpers(Devise::OmniAuth)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,19 +45,5 @@ module Devise
|
||||
"to your models and comment the current value in the config/initializers/devise.rb"
|
||||
end
|
||||
end
|
||||
|
||||
# Check all available mappings and only load related controllers.
|
||||
def eager_load!
|
||||
mappings = Devise.mappings.values.map(&:modules).flatten.uniq
|
||||
controllers = Devise::CONTROLLERS.values_at(*mappings)
|
||||
path = paths.is_a?(Hash) ? paths["app/controllers"].first : paths.app.controllers.first
|
||||
matcher = /\A#{Regexp.escape(path)}\/(.*)\.rb\Z/
|
||||
|
||||
Dir.glob("#{path}/devise/{#{controllers.join(',')}}_controller.rb").sort.each do |file|
|
||||
require_dependency file.sub(matcher, '\1')
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,6 +123,30 @@ module ActionDispatch::Routing
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# ==== Adding custom actions to override controllers
|
||||
#
|
||||
# You can pass a block to devise_for that will add any routes defined in the block to Devise's
|
||||
# list of known actions. This is important if you add a custom action to a controller that
|
||||
# overrides an out of the box Devise controller.
|
||||
# For example:
|
||||
#
|
||||
# class RegistrationsController < Devise::RegistrationsController
|
||||
# def update
|
||||
# # do something different here
|
||||
# end
|
||||
#
|
||||
# def deactivate
|
||||
# # not a standard action
|
||||
# # deactivate code here
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# In order to get Devise to recognize the deactivate action, your devise_for entry should look like this,
|
||||
#
|
||||
# devise_for :owners, :controllers => { :registrations => "registrations" } do
|
||||
# post "deactivate", :to => "registrations#deactivate", :as => "deactivate_registration"
|
||||
# end
|
||||
#
|
||||
def devise_for(*resources)
|
||||
options = resources.extract_options!
|
||||
|
||||
@@ -238,9 +262,17 @@ module ActionDispatch::Routing
|
||||
end
|
||||
end
|
||||
|
||||
def devise_oauth_callback(mapping, controllers) #:nodoc:
|
||||
get "/oauth/:action/callback", :action => Regexp.union(mapping.to.oauth_providers.map(&:to_s)),
|
||||
:to => controllers[:oauth_callbacks], :as => :oauth_callback
|
||||
def devise_omniauth_callback(mapping, controllers) #:nodoc:
|
||||
path_prefix = "/#{mapping.path}/auth"
|
||||
|
||||
if ::OmniAuth.config.path_prefix && ::OmniAuth.config.path_prefix != path_prefix
|
||||
warn "[DEVISE] You can only add :omniauthable behavior to one model."
|
||||
else
|
||||
::OmniAuth.config.path_prefix = path_prefix
|
||||
end
|
||||
|
||||
match "/auth/:action/callback", :action => Regexp.union(mapping.to.omniauth_providers.map(&:to_s)),
|
||||
:to => controllers[:omniauth_callbacks], :as => :omniauth_callback
|
||||
end
|
||||
|
||||
def with_devise_exclusive_scope(new_path, new_as) #:nodoc:
|
||||
|
||||
@@ -4,7 +4,7 @@ module Devise
|
||||
def model_contents
|
||||
<<-CONTENT
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :oauthable
|
||||
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
|
||||
devise :database_authenticatable, :registerable,
|
||||
:recoverable, :rememberable, :trackable, :validatable
|
||||
|
||||
|
||||
@@ -141,21 +141,20 @@ Devise.setup do |config|
|
||||
# Lists the formats that should be treated as navigational. Formats like
|
||||
# :html, should redirect to the sign in page when the user does not have
|
||||
# access, but formats like :xml or :json, should return 401.
|
||||
#
|
||||
# If you have any extra navigational formats, like :iphone or :mobile, you
|
||||
# should add them to the navigational formats lists. Default is [:html]
|
||||
# config.navigational_formats = [:html, :iphone]
|
||||
# should add them to the navigational formats lists.
|
||||
#
|
||||
# The :"*/*" format below is required to match Internet Explorer requests.
|
||||
# config.navigational_formats = [:"*/*", :html]
|
||||
|
||||
# The default HTTP method used to sign out a resource. Default is :get.
|
||||
# config.sign_out_via = :get
|
||||
|
||||
# ==> OAuth2
|
||||
# Add a new OAuth2 provider. Check the README for more information on setting
|
||||
# up on your models and hooks. By default this is not set.
|
||||
# config.oauth :github, 'APP_ID', 'APP_SECRET',
|
||||
# :site => 'https://github.com/',
|
||||
# :authorize_path => '/login/oauth/authorize',
|
||||
# :access_token_path => '/login/oauth/access_token',
|
||||
# :scope => %w(user public_repo)
|
||||
# ==> OmniAuth
|
||||
# Add a new OmniAuth provider. Check the wiki for more information on setting
|
||||
# up on your models and hooks.
|
||||
# config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
|
||||
|
||||
# ==> Warden configuration
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
|
||||
@@ -28,6 +28,11 @@ class FailureTest < ActiveSupport::TestCase
|
||||
assert_equal 302, @response.first
|
||||
end
|
||||
|
||||
test 'return 302 status for wildcard requests' do
|
||||
call_failure 'action_dispatch.request.formats' => nil, 'HTTP_ACCEPT' => '*/*'
|
||||
assert_equal 302, @response.first
|
||||
end
|
||||
|
||||
test 'return to the default redirect location' do
|
||||
call_failure
|
||||
assert_equal 'You need to sign in or sign up before continuing.', @request.flash[:alert]
|
||||
|
||||
@@ -376,7 +376,7 @@ end
|
||||
|
||||
class AuthenticationSignOutViaTest < ActionController::IntegrationTest
|
||||
def sign_in!(scope)
|
||||
sign_in_as_user(:visit => send("new_#{scope}_session_path"))
|
||||
sign_in_as_admin(:visit => send("new_#{scope}_session_path"))
|
||||
assert warden.authenticated?(scope)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OAuthableIntegrationTest < ActionController::IntegrationTest
|
||||
FACEBOOK_INFO = {
|
||||
:username => 'usertest',
|
||||
:email => 'user@test.com'
|
||||
}
|
||||
|
||||
ACCESS_TOKEN = {
|
||||
:access_token => "plataformatec"
|
||||
}
|
||||
|
||||
setup do
|
||||
Devise::Oauth.short_circuit_authorizers!
|
||||
end
|
||||
|
||||
teardown do
|
||||
Devise::Oauth.unshort_circuit_authorizers!
|
||||
Devise::Oauth.reset_stubs!
|
||||
User.singleton_class.remove_possible_method(:find_for_github_oauth)
|
||||
end
|
||||
|
||||
def stub_github!
|
||||
def User.find_for_github_oauth(*); end
|
||||
|
||||
Devise::Oauth.stub!(:github) do |b|
|
||||
b.post('/login/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] }
|
||||
end
|
||||
end
|
||||
|
||||
def stub_facebook!(valid=true)
|
||||
data = valid ? FACEBOOK_INFO : FACEBOOK_INFO.except(:email)
|
||||
|
||||
Devise::Oauth.stub!(:facebook) do |b|
|
||||
b.post('/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] }
|
||||
b.get('/me?access_token=plataformatec') { [200, {}, data.to_json] }
|
||||
end
|
||||
end
|
||||
|
||||
test "[BASIC] setup with persisted user" do
|
||||
stub_facebook!
|
||||
|
||||
assert_difference "User.count", 1 do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_current_url "/"
|
||||
assert_contain "Successfully authorized from Facebook account."
|
||||
|
||||
assert warden.authenticated?(:user)
|
||||
assert_not warden.authenticated?(:admin)
|
||||
assert "plataformatec", warden.user(:user).facebook_token
|
||||
end
|
||||
|
||||
test "[BASIC] setup with not persisted user and follow up" do
|
||||
stub_facebook!(false)
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_contain "1 error prohibited this user from being saved"
|
||||
assert_contain "Email can't be blank"
|
||||
|
||||
assert_not warden.authenticated?(:user)
|
||||
assert_not warden.authenticated?(:admin)
|
||||
|
||||
fill_in "Email", :with => "user.form@test.com"
|
||||
click_button "Sign up"
|
||||
|
||||
assert_current_url "/"
|
||||
assert_contain "You have signed up successfully."
|
||||
assert_contain "Hello User user.form@test.com"
|
||||
|
||||
assert warden.authenticated?(:user)
|
||||
assert_not warden.authenticated?(:admin)
|
||||
assert "plataformatec", warden.user(:user).facebook_token
|
||||
end
|
||||
|
||||
test "[BASIC] setup updating an existing user in database" do
|
||||
stub_facebook!
|
||||
user = create_user
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_current_url "/"
|
||||
assert_contain "Successfully authorized from Facebook account."
|
||||
|
||||
assert_equal user, warden.user(:user)
|
||||
assert_equal "plataformatec", user.reload.facebook_token
|
||||
end
|
||||
|
||||
test "[BASIC] setup updating an existing user in session" do
|
||||
stub_facebook!
|
||||
|
||||
# Create an user and change his e-mail
|
||||
user = sign_in_as_user
|
||||
user.email = "another@test.com"
|
||||
user.save!
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_current_url "/"
|
||||
assert_contain "Successfully authorized from Facebook account."
|
||||
|
||||
assert_equal user, warden.user(:user)
|
||||
assert_equal "another@test.com", warden.user(:user).email
|
||||
assert_equal "plataformatec", user.reload.facebook_token
|
||||
end
|
||||
|
||||
test "[SESSION CLEANUP] ensures session is cleaned up after sign up" do
|
||||
stub_facebook!(false)
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_contain "1 error prohibited this user from being saved"
|
||||
fill_in "Email", :with => "user.form@test.com"
|
||||
click_button "Sign up"
|
||||
|
||||
assert_contain "You have signed up successfully."
|
||||
visit "/users/sign_out"
|
||||
|
||||
user = sign_in_as_user
|
||||
assert_nil warden.user(:user).facebook_token
|
||||
assert_equal user, warden.user(:user)
|
||||
end
|
||||
|
||||
test "[SESSION CLEANUP] ensures session is cleaned up on cancel" do
|
||||
stub_facebook!(false)
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_contain "1 error prohibited this user from being saved"
|
||||
visit "/users/cancel"
|
||||
|
||||
user = sign_in_as_user
|
||||
assert_nil warden.user(:user).facebook_token
|
||||
assert_equal user, warden.user(:user)
|
||||
end
|
||||
|
||||
test "[SESSION CLEANUP] ensures session is cleaned up on sign in" do
|
||||
stub_facebook!(false)
|
||||
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert_contain "1 error prohibited this user from being saved"
|
||||
|
||||
user = sign_in_as_user
|
||||
assert_nil warden.user(:user).facebook_token
|
||||
assert_equal user, warden.user(:user)
|
||||
end
|
||||
|
||||
test "[I18N] scopes messages based on oauth callback for success" do
|
||||
stub_facebook!
|
||||
|
||||
store_translations :en, :devise => { :oauth_callbacks => {
|
||||
:facebook => { :success => "Welcome facebooker" } } } do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
assert_contain "Welcome facebooker"
|
||||
end
|
||||
end
|
||||
|
||||
test "[I18N] scopes messages based on oauth callback and resource name for success" do
|
||||
stub_facebook!
|
||||
|
||||
store_translations :en, :devise => { :oauth_callbacks => {
|
||||
:user => { :facebook => { :success => "Welcome facebooker user" } },
|
||||
:facebook => { :success => "Welcome facebooker" } } } do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
assert_contain "Welcome facebooker user"
|
||||
end
|
||||
end
|
||||
|
||||
test "[FAILURE] shows 404 if no code or error are given as params" do
|
||||
assert_raise AbstractController::ActionNotFound do
|
||||
visit "/users/oauth/facebook/callback"
|
||||
end
|
||||
end
|
||||
|
||||
test "[FAILURE] raises an error if model does not implement a hook" do
|
||||
begin
|
||||
visit "/users/oauth/github/callback?code=123456"
|
||||
raise "Expected visit to raise an error"
|
||||
rescue Exception => e
|
||||
assert_match "User does not respond to find_for_github_oauth", e.message
|
||||
end
|
||||
end
|
||||
|
||||
test "[FAILURE] handles callback error parameter according to the specification" do
|
||||
visit "/users/oauth/facebook/callback?error=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain 'Could not authorize you from Facebook because "Access denied".'
|
||||
end
|
||||
|
||||
test "[FAILURE] handles callback error_reason just for Facebook compatibility" do
|
||||
visit "/users/oauth/facebook/callback?error_reason=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain 'Could not authorize you from Facebook because "Access denied".'
|
||||
end
|
||||
|
||||
test "[FAILURE][I18N] uses I18n for custom messages" do
|
||||
visit "/users/oauth/facebook/callback?error=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain 'Could not authorize you from Facebook because "Access denied"'
|
||||
end
|
||||
|
||||
test "[FAILURE][I18N] uses I18n with oauth callback scope for custom messages" do
|
||||
store_translations :en, :devise => { :oauth_callbacks => {
|
||||
:facebook => { :failure => "Access denied bro" } } } do
|
||||
visit "/users/oauth/facebook/callback?error=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain "Access denied bro"
|
||||
end
|
||||
end
|
||||
|
||||
test "[FAILURE][I18N] uses I18n with oauth callback scope and resource name for custom messages" do
|
||||
store_translations :en, :devise => { :oauth_callbacks => {
|
||||
:user => { :facebook => { :failure => "Access denied user" } },
|
||||
:facebook => { :failure => "Access denied bro" } } } do
|
||||
visit "/users/oauth/facebook/callback?error=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain "Access denied user"
|
||||
end
|
||||
end
|
||||
end
|
||||
107
test/integration/omniauthable_test.rb
Normal file
107
test/integration/omniauthable_test.rb
Normal file
@@ -0,0 +1,107 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OmniauthableIntegrationTest < ActionController::IntegrationTest
|
||||
FACEBOOK_INFO = {
|
||||
:id => '12345',
|
||||
:link => 'http://facebook.com/josevalim',
|
||||
:email => 'user@example.com',
|
||||
:first_name => 'Jose',
|
||||
:last_name => 'Valim',
|
||||
:website => 'http://blog.plataformatec.com.br'
|
||||
}
|
||||
|
||||
ACCESS_TOKEN = {
|
||||
:access_token => "plataformatec"
|
||||
}
|
||||
|
||||
setup do
|
||||
stub_facebook!
|
||||
Devise::OmniAuth.short_circuit_authorizers!
|
||||
end
|
||||
|
||||
teardown do
|
||||
Devise::OmniAuth.unshort_circuit_authorizers!
|
||||
Devise::OmniAuth.reset_stubs!
|
||||
end
|
||||
|
||||
def stub_facebook!
|
||||
Devise::OmniAuth.stub!(:facebook) do |b|
|
||||
b.post('/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] }
|
||||
b.get('/me?access_token=plataformatec') { [200, {}, FACEBOOK_INFO.to_json] }
|
||||
end
|
||||
end
|
||||
|
||||
test "can access omniauth.auth in the env hash" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
|
||||
assert_equal "12345", json["uid"]
|
||||
assert_equal "facebook", json["provider"]
|
||||
assert_equal "josevalim", json["user_info"]["nickname"]
|
||||
assert_equal FACEBOOK_INFO, json["extra"]["user_hash"].symbolize_keys
|
||||
assert_equal "plataformatec", json["credentials"]["token"]
|
||||
end
|
||||
|
||||
test "cleans up session on sign up" do
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert session["devise.facebook_data"]
|
||||
|
||||
assert_difference "User.count" do
|
||||
visit "/users/sign_up"
|
||||
fill_in "Password", :with => "123456"
|
||||
fill_in "Password confirmation", :with => "123456"
|
||||
click_button "Sign up"
|
||||
end
|
||||
|
||||
assert_current_url "/"
|
||||
assert_contain "You have signed up successfully."
|
||||
assert_contain "Hello User user@example.com"
|
||||
assert_not session["devise.facebook_data"]
|
||||
end
|
||||
|
||||
test "cleans up session on cancel" do
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert session["devise.facebook_data"]
|
||||
visit "/users/cancel"
|
||||
assert !session["devise.facebook_data"]
|
||||
end
|
||||
|
||||
test "cleans up session on sign in" do
|
||||
assert_no_difference "User.count" do
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with Facebook"
|
||||
end
|
||||
|
||||
assert session["devise.facebook_data"]
|
||||
user = sign_in_as_user
|
||||
assert !session["devise.facebook_data"]
|
||||
end
|
||||
|
||||
test "handles callback error parameter according to the specification" do
|
||||
visit "/users/auth/facebook/callback?error=access_denied"
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain 'Could not authorize you from Facebook because "Access denied".'
|
||||
end
|
||||
|
||||
test "handles other exceptions from omniauth" do
|
||||
Devise::OmniAuth.stub!(:facebook) do |b|
|
||||
b.post('/oauth/access_token') { [401, {}, {}.to_json] }
|
||||
end
|
||||
|
||||
visit "/users/sign_in"
|
||||
click_link "Sign in with facebook"
|
||||
|
||||
assert_current_url "/users/sign_in"
|
||||
assert_contain 'Could not authorize you from Facebook because "Invalid credentials".'
|
||||
end
|
||||
end
|
||||
@@ -29,7 +29,6 @@ class RegistrationTest < ActionController::IntegrationTest
|
||||
click_button 'Sign up'
|
||||
|
||||
assert_contain 'You have signed up successfully. However, we could not sign you in because your account is unconfirmed.'
|
||||
assert_contain 'Sign in'
|
||||
assert_not_contain 'You have to confirm your account before continuing'
|
||||
|
||||
assert_not warden.authenticated?(:user)
|
||||
@@ -168,13 +167,13 @@ class RegistrationTest < ActionController::IntegrationTest
|
||||
|
||||
test 'a user should be able to cancel sign up by deleting data in the session' do
|
||||
get "/set"
|
||||
assert_equal "something", @request.session["user_provider_oauth_token"]
|
||||
assert_equal "something", @request.session["devise.foo_bar"]
|
||||
|
||||
get "/users/sign_up"
|
||||
assert_equal "something", @request.session["user_provider_oauth_token"]
|
||||
assert_equal "something", @request.session["devise.foo_bar"]
|
||||
|
||||
get "/users/cancel"
|
||||
assert_nil @request.session["user_provider_oauth_token"]
|
||||
assert_nil @request.session["devise.foo_bar"]
|
||||
assert_redirected_to new_user_registration_path
|
||||
end
|
||||
end
|
||||
|
||||
@@ -90,6 +90,20 @@ class MappingTest < ActiveSupport::TestCase
|
||||
assert mapping.recoverable?
|
||||
assert mapping.lockable?
|
||||
assert_not mapping.confirmable?
|
||||
assert_not mapping.oauthable?
|
||||
assert_not mapping.omniauthable?
|
||||
end
|
||||
|
||||
test 'find mapping by path' do
|
||||
assert_raise RuntimeError do
|
||||
Devise::Mapping.find_by_path!('/accounts/facebook/callback')
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
Devise::Mapping.find_by_path!('/:locale/accounts/login')
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
Devise::Mapping.find_by_path!('/accounts/facebook/callback', :path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OauthableTest < ActiveSupport::TestCase
|
||||
teardown { Devise::Oauth.reset_stubs! }
|
||||
|
||||
test "oauth_configs returns all configurations relative to that model" do
|
||||
swap User, :oauth_providers => [:github] do
|
||||
assert_equal User.oauth_configs, Devise.oauth_configs.slice(:github)
|
||||
end
|
||||
end
|
||||
|
||||
test "oauth_access_token returns the token object for the given provider" do
|
||||
Devise::Oauth.stub!(:facebook) do |b|
|
||||
b.get('/me?access_token=plataformatec') { [200, {}, {}.to_json] }
|
||||
end
|
||||
|
||||
access_token = User.oauth_access_token(:facebook, "plataformatec")
|
||||
assert_kind_of OAuth2::AccessToken, access_token
|
||||
assert_equal "{}", access_token.get("/me")
|
||||
end
|
||||
end
|
||||
@@ -1,44 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OauthConfigTest < ActiveSupport::TestCase
|
||||
ACCESS_TOKEN = {
|
||||
:access_token => "plataformatec"
|
||||
}
|
||||
|
||||
setup { @config = Devise.oauth_configs[:facebook] }
|
||||
teardown { Devise::Oauth.reset_stubs! }
|
||||
|
||||
test "stored OAuth2::Client" do
|
||||
assert_kind_of OAuth2::Client, @config.client
|
||||
end
|
||||
|
||||
test "build authorize url" do
|
||||
url = @config.authorize_url(:redirect_uri => "foo")
|
||||
assert_match "https://graph.facebook.com/oauth/authorize?", url
|
||||
assert_match "scope=email%2Coffline_access", url
|
||||
assert_match "client_id=APP_ID", url
|
||||
assert_match "type=web_server", url
|
||||
assert_match "redirect_uri=foo", url
|
||||
end
|
||||
|
||||
test "retrieves access token object by code" do
|
||||
Devise::Oauth.stub!(:facebook) do |b|
|
||||
b.post('/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] }
|
||||
b.get('/me?access_token=plataformatec') { [200, {}, {}.to_json] }
|
||||
end
|
||||
|
||||
access_token = @config.access_token_by_code("12345")
|
||||
assert_kind_of OAuth2::AccessToken, access_token
|
||||
assert_equal "{}", access_token.get("/me")
|
||||
end
|
||||
|
||||
test "retrieves access token object by token" do
|
||||
Devise::Oauth.stub!(:facebook) do |b|
|
||||
b.get('/me?access_token=plataformatec') { [200, {}, {}.to_json] }
|
||||
end
|
||||
|
||||
access_token = @config.access_token_by_token("plataformatec")
|
||||
assert_kind_of OAuth2::AccessToken, access_token
|
||||
assert_equal "{}", access_token.get("/me")
|
||||
end
|
||||
end
|
||||
@@ -1,47 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OauthRoutesTest < ActionController::TestCase
|
||||
tests ApplicationController
|
||||
|
||||
def assert_path_and_url(action, provider)
|
||||
# Resource param
|
||||
assert_equal @controller.send(action, :user, provider),
|
||||
@controller.send("user_#{action}", provider)
|
||||
|
||||
# Default url params
|
||||
assert_equal @controller.send(action, :user, provider, :param => 123),
|
||||
@controller.send("user_#{action}", provider, :param => 123)
|
||||
|
||||
# With an object
|
||||
assert_equal @controller.send(action, User.new, provider, :param => 123),
|
||||
@controller.send("user_#{action}", provider, :param => 123)
|
||||
end
|
||||
|
||||
test 'should alias oauth_callback to mapped user auth_callback' do
|
||||
assert_path_and_url :oauth_callback_path, :github
|
||||
assert_path_and_url :oauth_callback_url, :github
|
||||
assert_path_and_url :oauth_callback_path, :facebook
|
||||
assert_path_and_url :oauth_callback_url, :facebook
|
||||
end
|
||||
|
||||
test 'should alias oauth_authorize to mapped user auth_authorize' do
|
||||
assert_path_and_url :oauth_authorize_url, :github
|
||||
assert_path_and_url :oauth_authorize_url, :facebook
|
||||
end
|
||||
|
||||
test 'should adds scope, provider and redirect_uri to authorize urls' do
|
||||
url = @controller.oauth_authorize_url(:user, :github)
|
||||
assert_match "https://github.com/login/oauth/authorize?", url
|
||||
assert_match "scope=user%2Cpublic_repo", url
|
||||
assert_match "client_id=APP_ID", url
|
||||
assert_match "type=web_server", url
|
||||
assert_match "redirect_uri=http%3A%2F%2Ftest.host%2Fusers%2Foauth%2Fgithub%2Fcallback", url
|
||||
|
||||
url = @controller.oauth_authorize_url(:user, :facebook)
|
||||
assert_match "https://graph.facebook.com/oauth/authorize?", url
|
||||
assert_match "scope=email%2Coffline_access", url
|
||||
assert_match "client_id=APP_ID", url
|
||||
assert_match "type=web_server", url
|
||||
assert_match "redirect_uri=http%3A%2F%2Ftest.host%2Fusers%2Foauth%2Ffacebook%2Fcallback", url
|
||||
end
|
||||
end
|
||||
47
test/omniauth/url_helpers_test.rb
Normal file
47
test/omniauth/url_helpers_test.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
require 'test_helper'
|
||||
|
||||
class OmniAuthRoutesTest < ActionController::TestCase
|
||||
tests ApplicationController
|
||||
|
||||
def assert_path(action, provider, with_param=true)
|
||||
# Resource param
|
||||
assert_equal @controller.send(action, :user, provider),
|
||||
@controller.send("user_#{action}", provider)
|
||||
|
||||
# With an object
|
||||
assert_equal @controller.send(action, User.new, provider),
|
||||
@controller.send("user_#{action}", provider)
|
||||
|
||||
if with_param
|
||||
# Default url params
|
||||
assert_equal @controller.send(action, :user, provider, :param => 123),
|
||||
@controller.send("user_#{action}", provider, :param => 123)
|
||||
end
|
||||
end
|
||||
|
||||
test 'should alias omniauth_callback to mapped user auth_callback' do
|
||||
assert_path :omniauth_callback_path, :facebook
|
||||
end
|
||||
|
||||
test 'should alias omniauth_authorize to mapped user auth_authorize' do
|
||||
assert_path :omniauth_authorize_path, :facebook, false
|
||||
end
|
||||
|
||||
test 'should generate authorization path' do
|
||||
assert_match "/users/auth/facebook", @controller.omniauth_authorize_path(:user, :facebook)
|
||||
|
||||
assert_raise ArgumentError do
|
||||
@controller.omniauth_authorize_path(:user, :github)
|
||||
end
|
||||
end
|
||||
|
||||
test 'should generate authorization path with params' do
|
||||
assert_match "/users/auth/open_id?openid_url=http%3A%2F%2Fyahoo.com",
|
||||
@controller.omniauth_authorize_path(:user, :open_id, :openid_url => "http://yahoo.com")
|
||||
end
|
||||
|
||||
test 'should not add a "?" if no param was sent' do
|
||||
assert_equal "/users/auth/open_id",
|
||||
@controller.omniauth_authorize_path(:user, :open_id)
|
||||
end
|
||||
end
|
||||
@@ -6,7 +6,7 @@ class HomeController < ApplicationController
|
||||
end
|
||||
|
||||
def set
|
||||
session["user_provider_oauth_token"] = "something"
|
||||
session["devise.foo_bar"] = "something"
|
||||
head :ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
def facebook
|
||||
data = env["omniauth.auth"]
|
||||
session["devise.facebook_data"] = data["extra"]["user_hash"]
|
||||
render :json => data
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1 @@
|
||||
Home!
|
||||
|
||||
<%- User.oauth_providers.each do |provider| %>
|
||||
<%= link_to "Sign in with #{provider.to_s.titleize}", user_oauth_authorize_url(provider) %><br />
|
||||
<% end -%>
|
||||
Home!
|
||||
@@ -148,18 +148,9 @@ Devise.setup do |config|
|
||||
# The default HTTP method used to sign out a resource. Default is :get.
|
||||
# config.sign_out_via = :get
|
||||
|
||||
# ==> OAuth2
|
||||
# Add a new OAuth2 provider. Check the README for more information on setting
|
||||
# up on your models and hooks. By default this is not set.
|
||||
config.oauth :github, 'APP_ID', 'APP_SECRET',
|
||||
:site => 'https://github.com/',
|
||||
:authorize_path => '/login/oauth/authorize',
|
||||
:access_token_path => '/login/oauth/access_token',
|
||||
:scope => 'user,public_repo'
|
||||
|
||||
config.oauth :facebook, 'APP_ID', 'APP_SECRET',
|
||||
:site => 'https://graph.facebook.com/',
|
||||
:scope => %w(email offline_access)
|
||||
# ==> OmniAuth
|
||||
config.omniauth :facebook, 'APP_ID', 'APP_SECRET', :scope => 'email,offline_access'
|
||||
config.omniauth :open_id
|
||||
|
||||
# ==> Warden configuration
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
|
||||
@@ -8,7 +8,7 @@ Rails.application.routes.draw do
|
||||
resources :admins, :only => [:index]
|
||||
|
||||
# Users scope
|
||||
devise_for :users do
|
||||
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } do
|
||||
match "/devise_for/sign_in", :to => "devise/sessions#new"
|
||||
end
|
||||
|
||||
@@ -30,11 +30,11 @@ Rails.application.routes.draw do
|
||||
|
||||
# Other routes for routing_test.rb
|
||||
namespace :publisher, :path_names => { :sign_in => "i_dont_care", :sign_out => "get_out" } do
|
||||
devise_for :accounts, :class_name => "User", :path_names => { :sign_in => "get_in" }
|
||||
devise_for :accounts, :class_name => "Admin", :path_names => { :sign_in => "get_in" }
|
||||
end
|
||||
|
||||
scope ":locale" do
|
||||
devise_for :accounts, :singular => "manager", :class_name => "User",
|
||||
devise_for :accounts, :singular => "manager", :class_name => "Admin",
|
||||
:path_names => {
|
||||
:sign_in => "login", :sign_out => "logout",
|
||||
:password => "secret", :confirmation => "verification",
|
||||
@@ -44,9 +44,9 @@ Rails.application.routes.draw do
|
||||
end
|
||||
|
||||
namespace :sign_out_via, :module => "devise" do
|
||||
devise_for :deletes, :sign_out_via => :delete, :class_name => "User"
|
||||
devise_for :posts, :sign_out_via => :post, :class_name => "User"
|
||||
devise_for :delete_or_posts, :sign_out_via => [:delete, :post], :class_name => "User"
|
||||
devise_for :deletes, :sign_out_via => :delete, :class_name => "Admin"
|
||||
devise_for :posts, :sign_out_via => :post, :class_name => "Admin"
|
||||
devise_for :delete_or_posts, :sign_out_via => [:delete, :post], :class_name => "Admin"
|
||||
end
|
||||
|
||||
match "/set", :to => "home#set"
|
||||
|
||||
@@ -4,45 +4,20 @@ module SharedUser
|
||||
included do
|
||||
devise :database_authenticatable, :confirmable, :lockable, :recoverable,
|
||||
:registerable, :rememberable, :timeoutable, :token_authenticatable,
|
||||
:trackable, :validatable, :oauthable
|
||||
:trackable, :validatable, :omniauthable
|
||||
|
||||
# They need to be included after Devise is called.
|
||||
extend ExtendMethods
|
||||
end
|
||||
|
||||
module ExtendMethods
|
||||
def find_for_facebook_oauth(access_token, signed_in_resource=nil)
|
||||
data = ActiveSupport::JSON.decode(access_token.get('/me'))
|
||||
user = signed_in_resource || User.find_by_email(data["email"]) || User.new
|
||||
user.update_with_facebook_oauth(access_token, data)
|
||||
user.save
|
||||
user
|
||||
end
|
||||
|
||||
def new_with_session(params, session)
|
||||
super.tap do |user|
|
||||
if session[:user_facebook_oauth_token]
|
||||
access_token = oauth_access_token(:facebook, session[:user_facebook_oauth_token])
|
||||
user.update_with_facebook_oauth(access_token)
|
||||
if data = session["devise.facebook_data"]
|
||||
user.email = data["email"]
|
||||
user.confirmed_at = Time.now
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_with_facebook_oauth(access_token, data=nil)
|
||||
data ||= ActiveSupport::JSON.decode(access_token.get('/me'))
|
||||
|
||||
self.username = data["username"] unless username.present?
|
||||
self.email = data["email"] unless email.present?
|
||||
|
||||
self.confirmed_at ||= Time.now
|
||||
self.facebook_token = access_token.token
|
||||
|
||||
unless encrypted_password.present?
|
||||
self.password = Devise.friendly_token[0, 10]
|
||||
self.password_confirmation = nil
|
||||
end
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,15 +91,13 @@ class DefaultRoutingTest < ActionController::TestCase
|
||||
assert_named_route "/users/cancel", :cancel_user_registration_path
|
||||
end
|
||||
|
||||
test 'map oauth callbacks' do
|
||||
assert_recognizes({:controller => 'devise/oauth_callbacks', :action => 'facebook'}, {:path => 'users/oauth/facebook/callback', :method => :get})
|
||||
assert_named_route "/users/oauth/facebook/callback", :user_oauth_callback_path, :facebook
|
||||
|
||||
assert_recognizes({:controller => 'devise/oauth_callbacks', :action => 'github'}, {:path => 'users/oauth/github/callback', :method => :get})
|
||||
assert_named_route "/users/oauth/github/callback", :user_oauth_callback_path, :github
|
||||
test 'map omniauth callbacks' do
|
||||
assert_recognizes({:controller => 'users/omniauth_callbacks', :action => 'facebook'}, {:path => 'users/auth/facebook/callback', :method => :get})
|
||||
assert_recognizes({:controller => 'users/omniauth_callbacks', :action => 'facebook'}, {:path => 'users/auth/facebook/callback', :method => :post})
|
||||
assert_named_route "/users/auth/facebook/callback", :user_omniauth_callback_path, :facebook
|
||||
|
||||
assert_raise ActionController::RoutingError do
|
||||
assert_recognizes({:controller => 'devise/oauth_callbacks', :action => 'twitter'}, {:path => 'users/oauth/twitter/callback', :method => :get})
|
||||
assert_recognizes({:controller => 'ysers/omniauth_callbacks', :action => 'twitter'}, {:path => 'users/auth/twitter/callback', :method => :get})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -137,14 +135,6 @@ class CustomizedRoutingTest < ActionController::TestCase
|
||||
assert_recognizes({:controller => 'devise/passwords', :action => 'new', :locale => 'en'}, '/en/accounts/secret/new')
|
||||
end
|
||||
|
||||
test 'map account with custom path name for confirmation' do
|
||||
assert_recognizes({:controller => 'devise/confirmations', :action => 'new', :locale => 'en'}, '/en/accounts/verification/new')
|
||||
end
|
||||
|
||||
test 'map account with custom path name for unlock' do
|
||||
assert_recognizes({:controller => 'devise/unlocks', :action => 'new', :locale => 'en'}, '/en/accounts/unblock/new')
|
||||
end
|
||||
|
||||
test 'map account with custom path name for registration' do
|
||||
assert_recognizes({:controller => 'devise/registrations', :action => 'new', :locale => 'en'}, '/en/accounts/management/register')
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@ Webrat.configure do |config|
|
||||
config.open_error_files = false
|
||||
end
|
||||
|
||||
Devise::Oauth.test_mode!
|
||||
Devise::OmniAuth.test_mode!
|
||||
|
||||
# Add support to load paths so we can overwrite broken webrat setup
|
||||
$:.unshift File.expand_path('../support', __FILE__)
|
||||
|
||||
Reference in New Issue
Block a user