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.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +952 -0
  4. data/Rakefile +35 -0
  5. data/app/controllers/devise_token_auth/application_controller.rb +76 -0
  6. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +43 -0
  7. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +165 -0
  8. data/app/controllers/devise_token_auth/confirmations_controller.rb +30 -0
  9. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +243 -0
  10. data/app/controllers/devise_token_auth/passwords_controller.rb +202 -0
  11. data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
  12. data/app/controllers/devise_token_auth/sessions_controller.rb +133 -0
  13. data/app/controllers/devise_token_auth/token_validations_controller.rb +29 -0
  14. data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
  15. data/app/models/devise_token_auth/concerns/user.rb +260 -0
  16. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +26 -0
  17. data/app/validators/email_validator.rb +21 -0
  18. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  19. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  20. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  21. data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
  22. data/config/initializers/devise.rb +196 -0
  23. data/config/locales/da-DK.yml +50 -0
  24. data/config/locales/de.yml +49 -0
  25. data/config/locales/en.yml +50 -0
  26. data/config/locales/es.yml +49 -0
  27. data/config/locales/fr.yml +49 -0
  28. data/config/locales/it.yml +46 -0
  29. data/config/locales/ja.yml +46 -0
  30. data/config/locales/nl.yml +30 -0
  31. data/config/locales/pl.yml +48 -0
  32. data/config/locales/pt-BR.yml +46 -0
  33. data/config/locales/pt.yml +48 -0
  34. data/config/locales/ro.yml +46 -0
  35. data/config/locales/ru.yml +50 -0
  36. data/config/locales/sq.yml +46 -0
  37. data/config/locales/uk.yml +59 -0
  38. data/config/locales/vi.yml +50 -0
  39. data/config/locales/zh-CN.yml +46 -0
  40. data/config/locales/zh-HK.yml +48 -0
  41. data/config/locales/zh-TW.yml +48 -0
  42. data/lib/devise_token_auth.rb +8 -0
  43. data/lib/devise_token_auth/controllers/helpers.rb +149 -0
  44. data/lib/devise_token_auth/controllers/url_helpers.rb +8 -0
  45. data/lib/devise_token_auth/engine.rb +90 -0
  46. data/lib/devise_token_auth/rails/routes.rb +114 -0
  47. data/lib/devise_token_auth/url.rb +37 -0
  48. data/lib/devise_token_auth/version.rb +3 -0
  49. data/lib/generators/devise_token_auth/USAGE +31 -0
  50. data/lib/generators/devise_token_auth/install_generator.rb +160 -0
  51. data/lib/generators/devise_token_auth/install_views_generator.rb +16 -0
  52. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +48 -0
  53. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +55 -0
  54. data/lib/generators/devise_token_auth/templates/user.rb +7 -0
  55. data/lib/tasks/devise_token_auth_tasks.rake +4 -0
  56. data/test/controllers/custom/custom_confirmations_controller_test.rb +21 -0
  57. data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +29 -0
  58. data/test/controllers/custom/custom_passwords_controller_test.rb +75 -0
  59. data/test/controllers/custom/custom_registrations_controller_test.rb +54 -0
  60. data/test/controllers/custom/custom_sessions_controller_test.rb +37 -0
  61. data/test/controllers/custom/custom_token_validations_controller_test.rb +40 -0
  62. data/test/controllers/demo_group_controller_test.rb +153 -0
  63. data/test/controllers/demo_mang_controller_test.rb +284 -0
  64. data/test/controllers/demo_user_controller_test.rb +601 -0
  65. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +129 -0
  66. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +371 -0
  67. data/test/controllers/devise_token_auth/passwords_controller_test.rb +649 -0
  68. data/test/controllers/devise_token_auth/registrations_controller_test.rb +878 -0
  69. data/test/controllers/devise_token_auth/sessions_controller_test.rb +500 -0
  70. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +90 -0
  71. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +194 -0
  72. data/test/controllers/overrides/confirmations_controller_test.rb +43 -0
  73. data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +49 -0
  74. data/test/controllers/overrides/passwords_controller_test.rb +66 -0
  75. data/test/controllers/overrides/registrations_controller_test.rb +40 -0
  76. data/test/controllers/overrides/sessions_controller_test.rb +33 -0
  77. data/test/controllers/overrides/token_validations_controller_test.rb +41 -0
  78. data/test/dummy/README.rdoc +28 -0
  79. data/test/dummy/app/controllers/application_controller.rb +16 -0
  80. data/test/dummy/app/controllers/auth_origin_controller.rb +5 -0
  81. data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
  82. data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +11 -0
  83. data/test/dummy/app/controllers/custom/passwords_controller.rb +40 -0
  84. data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
  85. data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
  86. data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
  87. data/test/dummy/app/controllers/demo_group_controller.rb +13 -0
  88. data/test/dummy/app/controllers/demo_mang_controller.rb +12 -0
  89. data/test/dummy/app/controllers/demo_user_controller.rb +25 -0
  90. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +26 -0
  91. data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +14 -0
  92. data/test/dummy/app/controllers/overrides/passwords_controller.rb +33 -0
  93. data/test/dummy/app/controllers/overrides/registrations_controller.rb +27 -0
  94. data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
  95. data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
  96. data/test/dummy/app/helpers/application_helper.rb +1065 -0
  97. data/test/dummy/app/models/evil_user.rb +3 -0
  98. data/test/dummy/app/models/lockable_user.rb +5 -0
  99. data/test/dummy/app/models/mang.rb +3 -0
  100. data/test/dummy/app/models/nice_user.rb +7 -0
  101. data/test/dummy/app/models/only_email_user.rb +5 -0
  102. data/test/dummy/app/models/scoped_user.rb +7 -0
  103. data/test/dummy/app/models/unconfirmable_user.rb +8 -0
  104. data/test/dummy/app/models/unregisterable_user.rb +7 -0
  105. data/test/dummy/app/models/user.rb +18 -0
  106. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  107. data/test/dummy/config.ru +16 -0
  108. data/test/dummy/config/application.rb +24 -0
  109. data/test/dummy/config/application.yml.bk +0 -0
  110. data/test/dummy/config/boot.rb +5 -0
  111. data/test/dummy/config/environment.rb +5 -0
  112. data/test/dummy/config/environments/development.rb +44 -0
  113. data/test/dummy/config/environments/production.rb +82 -0
  114. data/test/dummy/config/environments/test.rb +48 -0
  115. data/test/dummy/config/initializers/assets.rb +8 -0
  116. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  117. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  118. data/test/dummy/config/initializers/devise.rb +3 -0
  119. data/test/dummy/config/initializers/devise_token_auth.rb +22 -0
  120. data/test/dummy/config/initializers/figaro.rb +1 -0
  121. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  122. data/test/dummy/config/initializers/inflections.rb +16 -0
  123. data/test/dummy/config/initializers/mime_types.rb +4 -0
  124. data/test/dummy/config/initializers/omniauth.rb +8 -0
  125. data/test/dummy/config/initializers/session_store.rb +3 -0
  126. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  127. data/test/dummy/config/routes.rb +72 -0
  128. data/test/dummy/config/spring.rb +1 -0
  129. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +63 -0
  130. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +62 -0
  131. data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +6 -0
  132. data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +5 -0
  133. data/test/dummy/db/migrate/20140928231203_devise_token_auth_create_evil_users.rb +64 -0
  134. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +60 -0
  135. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +61 -0
  136. data/test/dummy/db/migrate/20150409095712_devise_token_auth_create_nice_users.rb +61 -0
  137. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +61 -0
  138. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +61 -0
  139. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +61 -0
  140. data/test/dummy/db/schema.rb +258 -0
  141. data/test/dummy/lib/migration_database_helper.rb +29 -0
  142. data/test/integration/navigation_test.rb +10 -0
  143. data/test/lib/devise_token_auth/url_test.rb +24 -0
  144. data/test/lib/generators/devise_token_auth/install_generator_test.rb +187 -0
  145. data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +23 -0
  146. data/test/models/only_email_user_test.rb +35 -0
  147. data/test/models/user_test.rb +169 -0
  148. data/test/test_helper.rb +77 -0
  149. metadata +342 -0
@@ -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