digix_devise_token_auth 0.1.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +952 -0
- data/Rakefile +35 -0
- data/app/controllers/devise_token_auth/application_controller.rb +76 -0
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +43 -0
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +165 -0
- data/app/controllers/devise_token_auth/confirmations_controller.rb +30 -0
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +243 -0
- data/app/controllers/devise_token_auth/passwords_controller.rb +202 -0
- data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
- data/app/controllers/devise_token_auth/sessions_controller.rb +133 -0
- data/app/controllers/devise_token_auth/token_validations_controller.rb +29 -0
- data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
- data/app/models/devise_token_auth/concerns/user.rb +260 -0
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +26 -0
- data/app/validators/email_validator.rb +21 -0
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
- data/config/initializers/devise.rb +196 -0
- data/config/locales/da-DK.yml +50 -0
- data/config/locales/de.yml +49 -0
- data/config/locales/en.yml +50 -0
- data/config/locales/es.yml +49 -0
- data/config/locales/fr.yml +49 -0
- data/config/locales/it.yml +46 -0
- data/config/locales/ja.yml +46 -0
- data/config/locales/nl.yml +30 -0
- data/config/locales/pl.yml +48 -0
- data/config/locales/pt-BR.yml +46 -0
- data/config/locales/pt.yml +48 -0
- data/config/locales/ro.yml +46 -0
- data/config/locales/ru.yml +50 -0
- data/config/locales/sq.yml +46 -0
- data/config/locales/uk.yml +59 -0
- data/config/locales/vi.yml +50 -0
- data/config/locales/zh-CN.yml +46 -0
- data/config/locales/zh-HK.yml +48 -0
- data/config/locales/zh-TW.yml +48 -0
- data/lib/devise_token_auth.rb +8 -0
- data/lib/devise_token_auth/controllers/helpers.rb +149 -0
- data/lib/devise_token_auth/controllers/url_helpers.rb +8 -0
- data/lib/devise_token_auth/engine.rb +90 -0
- data/lib/devise_token_auth/rails/routes.rb +114 -0
- data/lib/devise_token_auth/url.rb +37 -0
- data/lib/devise_token_auth/version.rb +3 -0
- data/lib/generators/devise_token_auth/USAGE +31 -0
- data/lib/generators/devise_token_auth/install_generator.rb +160 -0
- data/lib/generators/devise_token_auth/install_views_generator.rb +16 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +48 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +55 -0
- data/lib/generators/devise_token_auth/templates/user.rb +7 -0
- data/lib/tasks/devise_token_auth_tasks.rake +4 -0
- data/test/controllers/custom/custom_confirmations_controller_test.rb +21 -0
- data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +29 -0
- data/test/controllers/custom/custom_passwords_controller_test.rb +75 -0
- data/test/controllers/custom/custom_registrations_controller_test.rb +54 -0
- data/test/controllers/custom/custom_sessions_controller_test.rb +37 -0
- data/test/controllers/custom/custom_token_validations_controller_test.rb +40 -0
- data/test/controllers/demo_group_controller_test.rb +153 -0
- data/test/controllers/demo_mang_controller_test.rb +284 -0
- data/test/controllers/demo_user_controller_test.rb +601 -0
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +129 -0
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +371 -0
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +649 -0
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +878 -0
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +500 -0
- data/test/controllers/devise_token_auth/token_validations_controller_test.rb +90 -0
- data/test/controllers/devise_token_auth/unlocks_controller_test.rb +194 -0
- data/test/controllers/overrides/confirmations_controller_test.rb +43 -0
- data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +49 -0
- data/test/controllers/overrides/passwords_controller_test.rb +66 -0
- data/test/controllers/overrides/registrations_controller_test.rb +40 -0
- data/test/controllers/overrides/sessions_controller_test.rb +33 -0
- data/test/controllers/overrides/token_validations_controller_test.rb +41 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/app/controllers/application_controller.rb +16 -0
- data/test/dummy/app/controllers/auth_origin_controller.rb +5 -0
- data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
- data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +11 -0
- data/test/dummy/app/controllers/custom/passwords_controller.rb +40 -0
- data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
- data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
- data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
- data/test/dummy/app/controllers/demo_group_controller.rb +13 -0
- data/test/dummy/app/controllers/demo_mang_controller.rb +12 -0
- data/test/dummy/app/controllers/demo_user_controller.rb +25 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +26 -0
- data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +14 -0
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +33 -0
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +27 -0
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
- data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
- data/test/dummy/app/helpers/application_helper.rb +1065 -0
- data/test/dummy/app/models/evil_user.rb +3 -0
- data/test/dummy/app/models/lockable_user.rb +5 -0
- data/test/dummy/app/models/mang.rb +3 -0
- data/test/dummy/app/models/nice_user.rb +7 -0
- data/test/dummy/app/models/only_email_user.rb +5 -0
- data/test/dummy/app/models/scoped_user.rb +7 -0
- data/test/dummy/app/models/unconfirmable_user.rb +8 -0
- data/test/dummy/app/models/unregisterable_user.rb +7 -0
- data/test/dummy/app/models/user.rb +18 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +16 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/application.yml.bk +0 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +44 -0
- data/test/dummy/config/environments/production.rb +82 -0
- data/test/dummy/config/environments/test.rb +48 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/devise.rb +3 -0
- data/test/dummy/config/initializers/devise_token_auth.rb +22 -0
- data/test/dummy/config/initializers/figaro.rb +1 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/omniauth.rb +8 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/routes.rb +72 -0
- data/test/dummy/config/spring.rb +1 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +63 -0
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +62 -0
- data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +6 -0
- data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +5 -0
- data/test/dummy/db/migrate/20140928231203_devise_token_auth_create_evil_users.rb +64 -0
- data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +60 -0
- data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +61 -0
- data/test/dummy/db/migrate/20150409095712_devise_token_auth_create_nice_users.rb +61 -0
- data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +61 -0
- data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +61 -0
- data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +61 -0
- data/test/dummy/db/schema.rb +258 -0
- data/test/dummy/lib/migration_database_helper.rb +29 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/lib/devise_token_auth/url_test.rb +24 -0
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +187 -0
- data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +23 -0
- data/test/models/only_email_user_test.rb +35 -0
- data/test/models/user_test.rb +169 -0
- data/test/test_helper.rb +77 -0
- metadata +342 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'DeviseTokenAuth'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'lib'
|
|
28
|
+
t.libs << 'test'
|
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
|
30
|
+
t.verbose = false
|
|
31
|
+
t.warning = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
task default: :test
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class ApplicationController < DeviseController
|
|
3
|
+
include DeviseTokenAuth::Concerns::SetUserByToken
|
|
4
|
+
include DeviseTokenAuth::Concerns::ResourceFinder
|
|
5
|
+
|
|
6
|
+
def resource_data(opts={})
|
|
7
|
+
response_data = opts[:resource_json] || @resource.as_json
|
|
8
|
+
if json_api?
|
|
9
|
+
response_data['type'] = @resource.class.name.parameterize
|
|
10
|
+
end
|
|
11
|
+
response_data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def resource_errors
|
|
15
|
+
return @resource.errors.to_hash.merge(full_messages: @resource.errors.full_messages)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
def build_redirect_headers(access_token, client, redirect_header_options = {})
|
|
21
|
+
{
|
|
22
|
+
DeviseTokenAuth.headers_names[:"access-token"] => access_token,
|
|
23
|
+
DeviseTokenAuth.headers_names[:"client"] => client,
|
|
24
|
+
:config => params[:config],
|
|
25
|
+
|
|
26
|
+
# Legacy parameters which may be removed in a future release.
|
|
27
|
+
# Consider using "client" and "access-token" in client code.
|
|
28
|
+
# See: github.com/lynndylanhurley/devise_token_auth/issues/993
|
|
29
|
+
:client_id => client,
|
|
30
|
+
:token => access_token
|
|
31
|
+
}.merge(redirect_header_options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def params_for_resource(resource)
|
|
35
|
+
devise_parameter_sanitizer.instance_values['permitted'][resource].each do |type|
|
|
36
|
+
params[type.to_s] ||= request.headers[type.to_s] unless request.headers[type.to_s].nil?
|
|
37
|
+
end
|
|
38
|
+
devise_parameter_sanitizer.instance_values['permitted'][resource]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def resource_class(m=nil)
|
|
42
|
+
if m
|
|
43
|
+
mapping = Devise.mappings[m]
|
|
44
|
+
else
|
|
45
|
+
mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
mapping.to
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def json_api?
|
|
52
|
+
return false unless defined?(ActiveModel::Serializer)
|
|
53
|
+
return ActiveModel::Serializer.setup do |config|
|
|
54
|
+
config.adapter == :json_api
|
|
55
|
+
end if ActiveModel::Serializer.respond_to?(:setup)
|
|
56
|
+
return ActiveModelSerializers.config.adapter == :json_api
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def recoverable_enabled?
|
|
60
|
+
resource_class.devise_modules.include?(:recoverable)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def confirmable_enabled?
|
|
64
|
+
resource_class.devise_modules.include?(:confirmable)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_error(status, message, data = nil)
|
|
68
|
+
response = {
|
|
69
|
+
success: false,
|
|
70
|
+
errors: [message]
|
|
71
|
+
}
|
|
72
|
+
response = response.merge(data) if data
|
|
73
|
+
render json: response, status: status
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module DeviseTokenAuth::Concerns::ResourceFinder
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
include DeviseTokenAuth::Controllers::Helpers
|
|
4
|
+
|
|
5
|
+
def get_case_insensitive_field_from_resource_params(field)
|
|
6
|
+
# honor Devise configuration for case_insensitive keys
|
|
7
|
+
q_value = resource_params[field.to_sym]
|
|
8
|
+
|
|
9
|
+
if resource_class.case_insensitive_keys.include?(field.to_sym)
|
|
10
|
+
q_value.downcase!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if resource_class.strip_whitespace_keys.include?(field.to_sym)
|
|
14
|
+
q_value.strip!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
q_value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def find_resource(field, value)
|
|
21
|
+
# fix for mysql default case insensitivity
|
|
22
|
+
q = "#{field.to_s} = ? AND provider='#{provider.to_s}'"
|
|
23
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
|
|
24
|
+
q = "BINARY " + q
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@resource = resource_class.where(q, value).first
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def resource_class(m=nil)
|
|
31
|
+
if m
|
|
32
|
+
mapping = Devise.mappings[m]
|
|
33
|
+
else
|
|
34
|
+
mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
mapping.to
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def provider
|
|
41
|
+
'email'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module DeviseTokenAuth::Concerns::SetUserByToken
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
include DeviseTokenAuth::Concerns::ResourceFinder
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action :set_request_start
|
|
7
|
+
after_action :update_auth_header
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
# keep track of request duration
|
|
13
|
+
def set_request_start
|
|
14
|
+
@request_started_at = Time.now
|
|
15
|
+
@used_auth_by_token = true
|
|
16
|
+
|
|
17
|
+
# initialize instance variables
|
|
18
|
+
@client_id = nil
|
|
19
|
+
@resource = nil
|
|
20
|
+
@token = nil
|
|
21
|
+
@is_batch_request = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ensure_pristine_resource
|
|
25
|
+
if @resource.changed?
|
|
26
|
+
# Stash pending changes in the resource before reloading.
|
|
27
|
+
changes = @resource.changes
|
|
28
|
+
@resource.reload
|
|
29
|
+
end
|
|
30
|
+
yield
|
|
31
|
+
ensure
|
|
32
|
+
# Reapply pending changes
|
|
33
|
+
@resource.assign_attributes(changes) if changes
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# user auth
|
|
37
|
+
def set_user_by_token(mapping=nil)
|
|
38
|
+
# determine target authentication class
|
|
39
|
+
rc = resource_class(mapping)
|
|
40
|
+
|
|
41
|
+
# no default user defined
|
|
42
|
+
return unless rc
|
|
43
|
+
|
|
44
|
+
# gets the headers names, which was set in the initialize file
|
|
45
|
+
uid_name = DeviseTokenAuth.headers_names[:'uid']
|
|
46
|
+
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
|
|
47
|
+
client_name = DeviseTokenAuth.headers_names[:'client']
|
|
48
|
+
|
|
49
|
+
# parse header for values necessary for authentication
|
|
50
|
+
uid = request.headers[uid_name] || params[uid_name]
|
|
51
|
+
@token ||= request.headers[access_token_name] || params[access_token_name]
|
|
52
|
+
@client_id ||= request.headers[client_name] || params[client_name]
|
|
53
|
+
|
|
54
|
+
# client_id isn't required, set to 'default' if absent
|
|
55
|
+
@client_id ||= 'default'
|
|
56
|
+
|
|
57
|
+
# check for an existing user, authenticated via warden/devise, if enabled
|
|
58
|
+
if DeviseTokenAuth.enable_standard_devise_support
|
|
59
|
+
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
|
|
60
|
+
if devise_warden_user && devise_warden_user.tokens[@client_id].nil?
|
|
61
|
+
@used_auth_by_token = false
|
|
62
|
+
@resource = devise_warden_user
|
|
63
|
+
@resource.create_new_auth_token
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# user has already been found and authenticated
|
|
68
|
+
return @resource if @resource && @resource.is_a?(rc)
|
|
69
|
+
|
|
70
|
+
# ensure we clear the client_id
|
|
71
|
+
if !@token
|
|
72
|
+
@client_id = nil
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return false unless @token
|
|
77
|
+
|
|
78
|
+
# mitigate timing attacks by finding by uid instead of auth token
|
|
79
|
+
user = uid && rc.find_by(uid: uid)
|
|
80
|
+
|
|
81
|
+
if user && user.valid_token?(@token, @client_id)
|
|
82
|
+
# sign_in with bypass: true will be deprecated in the next version of Devise
|
|
83
|
+
if self.respond_to?(:bypass_sign_in) && DeviseTokenAuth.bypass_sign_in
|
|
84
|
+
bypass_sign_in(user, scope: :user)
|
|
85
|
+
else
|
|
86
|
+
sign_in(:user, user, store: false, event: :fetch, bypass: DeviseTokenAuth.bypass_sign_in)
|
|
87
|
+
end
|
|
88
|
+
return @resource = user
|
|
89
|
+
else
|
|
90
|
+
# zero all values previously set values
|
|
91
|
+
@client_id = nil
|
|
92
|
+
return @resource = nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def update_auth_header
|
|
97
|
+
# cannot save object if model has invalid params
|
|
98
|
+
return unless defined?(@resource) && @resource && @resource.valid? && @client_id
|
|
99
|
+
|
|
100
|
+
# Generate new client_id with existing authentication
|
|
101
|
+
@client_id = nil unless @used_auth_by_token
|
|
102
|
+
|
|
103
|
+
if @used_auth_by_token && !DeviseTokenAuth.change_headers_on_each_request
|
|
104
|
+
# should not append auth header if @resource related token was
|
|
105
|
+
# cleared by sign out in the meantime
|
|
106
|
+
return if @resource.reload.tokens[@client_id].nil?
|
|
107
|
+
|
|
108
|
+
auth_header = @resource.build_auth_header(@token, @client_id)
|
|
109
|
+
|
|
110
|
+
# update the response header
|
|
111
|
+
response.headers.merge!(auth_header)
|
|
112
|
+
|
|
113
|
+
else
|
|
114
|
+
|
|
115
|
+
ensure_pristine_resource do
|
|
116
|
+
# Lock the user record during any auth_header updates to ensure
|
|
117
|
+
# we don't have write contention from multiple threads
|
|
118
|
+
@resource.with_lock do
|
|
119
|
+
# should not append auth header if @resource related token was
|
|
120
|
+
# cleared by sign out in the meantime
|
|
121
|
+
return if @used_auth_by_token && @resource.tokens[@client_id].nil?
|
|
122
|
+
|
|
123
|
+
# determine batch request status after request processing, in case
|
|
124
|
+
# another processes has updated it during that processing
|
|
125
|
+
@is_batch_request = is_batch_request?(@resource, @client_id)
|
|
126
|
+
|
|
127
|
+
auth_header = {}
|
|
128
|
+
|
|
129
|
+
# extend expiration of batch buffer to account for the duration of
|
|
130
|
+
# this request
|
|
131
|
+
if @is_batch_request
|
|
132
|
+
auth_header = @resource.extend_batch_buffer(@token, @client_id)
|
|
133
|
+
|
|
134
|
+
# Do not return token for batch requests to avoid invalidated
|
|
135
|
+
# tokens returned to the client in case of race conditions.
|
|
136
|
+
# Use a blank string for the header to still be present and
|
|
137
|
+
# being passed in a XHR response in case of
|
|
138
|
+
# 304 Not Modified responses.
|
|
139
|
+
auth_header[DeviseTokenAuth.headers_names[:"access-token"]] = ' '
|
|
140
|
+
auth_header[DeviseTokenAuth.headers_names[:"expiry"]] = ' '
|
|
141
|
+
|
|
142
|
+
# update Authorization response header with new token
|
|
143
|
+
else
|
|
144
|
+
auth_header = @resource.create_new_auth_token(@client_id)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# update the response header
|
|
148
|
+
response.headers.merge!(auth_header)
|
|
149
|
+
|
|
150
|
+
end # end lock
|
|
151
|
+
end # end ensure_pristine_resource
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def is_batch_request?(user, client_id)
|
|
160
|
+
!params[:unbatch] &&
|
|
161
|
+
user.tokens[client_id] &&
|
|
162
|
+
user.tokens[client_id]['updated_at'] &&
|
|
163
|
+
Time.parse(user.tokens[client_id]['updated_at']) > @request_started_at - DeviseTokenAuth.batch_request_buffer_throttle
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class ConfirmationsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
def show
|
|
4
|
+
@resource = resource_class.confirm_by_token(params[:confirmation_token])
|
|
5
|
+
|
|
6
|
+
if @resource && @resource.id
|
|
7
|
+
expiry = nil
|
|
8
|
+
if defined?(@resource.sign_in_count) && @resource.sign_in_count > 0
|
|
9
|
+
expiry = (Time.now + 1.second).to_i
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
client_id, token = @resource.create_token expiry: expiry
|
|
13
|
+
|
|
14
|
+
sign_in(@resource)
|
|
15
|
+
@resource.save!
|
|
16
|
+
|
|
17
|
+
yield @resource if block_given?
|
|
18
|
+
|
|
19
|
+
redirect_header_options = {account_confirmation_success: true}
|
|
20
|
+
redirect_headers = build_redirect_headers(token,
|
|
21
|
+
client_id,
|
|
22
|
+
redirect_header_options)
|
|
23
|
+
redirect_to(@resource.build_auth_url(params[:redirect_url],
|
|
24
|
+
redirect_headers))
|
|
25
|
+
else
|
|
26
|
+
raise ActionController::RoutingError.new('Not Found')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
|
|
4
|
+
attr_reader :auth_params
|
|
5
|
+
skip_before_action :set_user_by_token, raise: false
|
|
6
|
+
skip_after_action :update_auth_header
|
|
7
|
+
|
|
8
|
+
# intermediary route for successful omniauth authentication. omniauth does
|
|
9
|
+
# not support multiple models, so we must resort to this terrible hack.
|
|
10
|
+
def redirect_callbacks
|
|
11
|
+
|
|
12
|
+
# derive target redirect route from 'resource_class' param, which was set
|
|
13
|
+
# before authentication.
|
|
14
|
+
devise_mapping = [request.env['omniauth.params']['namespace_name'],
|
|
15
|
+
request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
|
|
16
|
+
path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
|
|
17
|
+
klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
|
|
18
|
+
redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
|
|
19
|
+
|
|
20
|
+
# preserve omniauth info for success route. ignore 'extra' in twitter
|
|
21
|
+
# auth response to avoid CookieOverflow.
|
|
22
|
+
session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
|
|
23
|
+
session['dta.omniauth.params'] = request.env['omniauth.params']
|
|
24
|
+
|
|
25
|
+
redirect_to redirect_route
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def omniauth_success
|
|
29
|
+
get_resource_from_auth_hash
|
|
30
|
+
set_token_on_resource
|
|
31
|
+
create_auth_params
|
|
32
|
+
|
|
33
|
+
if confirmable_enabled?
|
|
34
|
+
# don't send confirmation email!!!
|
|
35
|
+
@resource.skip_confirmation!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sign_in(:user, @resource, store: false, bypass: false)
|
|
39
|
+
|
|
40
|
+
@resource.save!
|
|
41
|
+
|
|
42
|
+
yield @resource if block_given?
|
|
43
|
+
|
|
44
|
+
render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def omniauth_failure
|
|
48
|
+
@error = params[:message]
|
|
49
|
+
render_data_or_redirect('authFailure', {error: @error})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
protected
|
|
53
|
+
|
|
54
|
+
# this will be determined differently depending on the action that calls
|
|
55
|
+
# it. redirect_callbacks is called upon returning from successful omniauth
|
|
56
|
+
# authentication, and the target params live in an omniauth-specific
|
|
57
|
+
# request.env variable. this variable is then persisted thru the redirect
|
|
58
|
+
# using our own dta.omniauth.params session var. the omniauth_success
|
|
59
|
+
# method will access that session var and then destroy it immediately
|
|
60
|
+
# after use. In the failure case, finally, the omniauth params
|
|
61
|
+
# are added as query params in our monkey patch to OmniAuth in engine.rb
|
|
62
|
+
def omniauth_params
|
|
63
|
+
if !defined?(@_omniauth_params)
|
|
64
|
+
if request.env['omniauth.params'] && request.env['omniauth.params'].any?
|
|
65
|
+
@_omniauth_params = request.env['omniauth.params']
|
|
66
|
+
elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
|
|
67
|
+
@_omniauth_params ||= session.delete('dta.omniauth.params')
|
|
68
|
+
@_omniauth_params
|
|
69
|
+
elsif params['omniauth_window_type']
|
|
70
|
+
@_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
|
|
71
|
+
else
|
|
72
|
+
@_omniauth_params = {}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
@_omniauth_params
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# break out provider attribute assignment for easy method extension
|
|
80
|
+
def assign_provider_attrs(user, auth_hash)
|
|
81
|
+
attrs = auth_hash['info'].slice(*user.attributes.keys)
|
|
82
|
+
user.assign_attributes(attrs)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# derive allowed params from the standard devise parameter sanitizer
|
|
86
|
+
def whitelisted_params
|
|
87
|
+
whitelist = params_for_resource(:sign_up)
|
|
88
|
+
|
|
89
|
+
whitelist.inject({}){|coll, key|
|
|
90
|
+
param = omniauth_params[key.to_s]
|
|
91
|
+
if param
|
|
92
|
+
coll[key] = param
|
|
93
|
+
end
|
|
94
|
+
coll
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def resource_class(mapping = nil)
|
|
99
|
+
if omniauth_params['resource_class']
|
|
100
|
+
omniauth_params['resource_class'].constantize
|
|
101
|
+
elsif params['resource_class']
|
|
102
|
+
params['resource_class'].constantize
|
|
103
|
+
else
|
|
104
|
+
raise "No resource_class found"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def resource_name
|
|
109
|
+
resource_class
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def omniauth_window_type
|
|
113
|
+
omniauth_params['omniauth_window_type']
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def auth_origin_url
|
|
117
|
+
omniauth_params['auth_origin_url'] || omniauth_params['origin']
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# in the success case, omniauth_window_type is in the omniauth_params.
|
|
121
|
+
# in the failure case, it is in a query param. See monkey patch above
|
|
122
|
+
def omniauth_window_type
|
|
123
|
+
omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# this sesison value is set by the redirect_callbacks method. its purpose
|
|
127
|
+
# is to persist the omniauth auth hash value thru a redirect. the value
|
|
128
|
+
# must be destroyed immediatly after it is accessed by omniauth_success
|
|
129
|
+
def auth_hash
|
|
130
|
+
@_auth_hash ||= session.delete('dta.omniauth.auth')
|
|
131
|
+
@_auth_hash
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ensure that this controller responds to :devise_controller? conditionals.
|
|
135
|
+
# this is used primarily for access to the parameter sanitizers.
|
|
136
|
+
def assert_is_devise_resource!
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# necessary for access to devise_parameter_sanitizers
|
|
141
|
+
def devise_mapping
|
|
142
|
+
if omniauth_params
|
|
143
|
+
Devise.mappings[[omniauth_params['namespace_name'],
|
|
144
|
+
omniauth_params['resource_class'].underscore].compact.join('_').to_sym]
|
|
145
|
+
else
|
|
146
|
+
request.env['devise.mapping']
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def set_random_password
|
|
151
|
+
# set crazy password for new oauth users. this is only used to prevent
|
|
152
|
+
# access via email sign-in.
|
|
153
|
+
p = SecureRandom.urlsafe_base64(nil, false)
|
|
154
|
+
@resource.password = p
|
|
155
|
+
@resource.password_confirmation = p
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def create_auth_params
|
|
159
|
+
@auth_params = {
|
|
160
|
+
auth_token: @token,
|
|
161
|
+
client_id: @client_id,
|
|
162
|
+
uid: @resource.uid,
|
|
163
|
+
expiry: @expiry,
|
|
164
|
+
config: @config
|
|
165
|
+
}
|
|
166
|
+
@auth_params.merge!(oauth_registration: true) if @oauth_registration
|
|
167
|
+
@auth_params
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def set_token_on_resource
|
|
171
|
+
@config = omniauth_params['config_name']
|
|
172
|
+
@client_id, @token, @expiry = @resource.create_token
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def render_data(message, data)
|
|
176
|
+
@data = data.merge({
|
|
177
|
+
message: message
|
|
178
|
+
})
|
|
179
|
+
render :layout => nil, :template => "devise_token_auth/omniauth_external_window"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def render_data_or_redirect(message, data, user_data = {})
|
|
183
|
+
|
|
184
|
+
# We handle inAppBrowser and newWindow the same, but it is nice
|
|
185
|
+
# to support values in case people need custom implementations for each case
|
|
186
|
+
# (For example, nbrustein does not allow new users to be created if logging in with
|
|
187
|
+
# an inAppBrowser)
|
|
188
|
+
#
|
|
189
|
+
# See app/views/devise_token_auth/omniauth_external_window.html.erb to understand
|
|
190
|
+
# why we can handle these both the same. The view is setup to handle both cases
|
|
191
|
+
# at the same time.
|
|
192
|
+
if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
|
|
193
|
+
render_data(message, user_data.merge(data))
|
|
194
|
+
|
|
195
|
+
elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
|
|
196
|
+
|
|
197
|
+
# build and redirect to destination url
|
|
198
|
+
redirect_to DeviseTokenAuth::Url.generate(auth_origin_url, data.merge(blank: true))
|
|
199
|
+
else
|
|
200
|
+
|
|
201
|
+
# there SHOULD always be an auth_origin_url, but if someone does something silly
|
|
202
|
+
# like coming straight to this url or refreshing the page at the wrong time, there may not be one.
|
|
203
|
+
# In that case, just render in plain text the error message if there is one or otherwise
|
|
204
|
+
# a generic message.
|
|
205
|
+
fallback_render data[:error] || 'An error occurred'
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def fallback_render(text)
|
|
210
|
+
render inline: %Q|
|
|
211
|
+
|
|
212
|
+
<html>
|
|
213
|
+
<head></head>
|
|
214
|
+
<body>
|
|
215
|
+
#{text}
|
|
216
|
+
</body>
|
|
217
|
+
</html>|
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def get_resource_from_auth_hash
|
|
221
|
+
# find or create user by provider and provider uid
|
|
222
|
+
@resource = resource_class.where({
|
|
223
|
+
uid: auth_hash['uid'],
|
|
224
|
+
provider: auth_hash['provider']
|
|
225
|
+
}).first_or_initialize
|
|
226
|
+
|
|
227
|
+
if @resource.new_record?
|
|
228
|
+
@oauth_registration = true
|
|
229
|
+
set_random_password
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# sync user info with provider, update/generate auth token
|
|
233
|
+
assign_provider_attrs(@resource, auth_hash)
|
|
234
|
+
|
|
235
|
+
# assign any additional (whitelisted) attributes
|
|
236
|
+
extra_params = whitelisted_params
|
|
237
|
+
@resource.assign_attributes(extra_params) if extra_params
|
|
238
|
+
|
|
239
|
+
@resource
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
end
|
|
243
|
+
end
|