devise_token_auth 1.0.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -2
  3. data/app/controllers/devise_token_auth/application_controller.rb +2 -3
  4. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +11 -12
  5. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +39 -55
  6. data/app/controllers/devise_token_auth/confirmations_controller.rb +63 -20
  7. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +77 -29
  8. data/app/controllers/devise_token_auth/passwords_controller.rb +44 -30
  9. data/app/controllers/devise_token_auth/registrations_controller.rb +33 -40
  10. data/app/controllers/devise_token_auth/sessions_controller.rb +5 -5
  11. data/app/controllers/devise_token_auth/unlocks_controller.rb +4 -4
  12. data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
  13. data/app/models/devise_token_auth/concerns/confirmable_support.rb +27 -0
  14. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  15. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
  16. data/app/models/devise_token_auth/concerns/user.rb +52 -71
  17. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +3 -3
  18. data/app/validators/{email_validator.rb → devise_token_auth_email_validator.rb} +1 -1
  19. data/config/locales/da-DK.yml +2 -0
  20. data/config/locales/de.yml +2 -0
  21. data/config/locales/en.yml +7 -0
  22. data/config/locales/es.yml +2 -0
  23. data/config/locales/fr.yml +2 -0
  24. data/config/locales/he.yml +52 -0
  25. data/config/locales/it.yml +2 -0
  26. data/config/locales/ja.yml +4 -2
  27. data/config/locales/ko.yml +51 -0
  28. data/config/locales/nl.yml +2 -0
  29. data/config/locales/pl.yml +6 -3
  30. data/config/locales/pt-BR.yml +2 -0
  31. data/config/locales/pt.yml +6 -3
  32. data/config/locales/ro.yml +2 -0
  33. data/config/locales/ru.yml +2 -0
  34. data/config/locales/sq.yml +2 -0
  35. data/config/locales/sv.yml +2 -0
  36. data/config/locales/uk.yml +2 -0
  37. data/config/locales/vi.yml +2 -0
  38. data/config/locales/zh-CN.yml +2 -0
  39. data/config/locales/zh-HK.yml +2 -0
  40. data/config/locales/zh-TW.yml +2 -0
  41. data/lib/devise_token_auth.rb +6 -3
  42. data/lib/devise_token_auth/blacklist.rb +2 -0
  43. data/lib/devise_token_auth/engine.rb +7 -1
  44. data/lib/devise_token_auth/rails/routes.rb +1 -1
  45. data/lib/devise_token_auth/token_factory.rb +126 -0
  46. data/lib/devise_token_auth/url.rb +3 -0
  47. data/lib/devise_token_auth/version.rb +1 -1
  48. data/lib/generators/devise_token_auth/USAGE +1 -1
  49. data/lib/generators/devise_token_auth/install_generator.rb +6 -90
  50. data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
  51. data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
  52. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +10 -0
  53. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +0 -7
  54. data/lib/generators/devise_token_auth/templates/user.rb.erb +2 -2
  55. data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
  56. data/test/controllers/custom/custom_confirmations_controller_test.rb +1 -1
  57. data/test/controllers/demo_user_controller_test.rb +2 -2
  58. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +83 -19
  59. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +109 -42
  60. data/test/controllers/devise_token_auth/passwords_controller_test.rb +227 -102
  61. data/test/controllers/devise_token_auth/registrations_controller_test.rb +34 -7
  62. data/test/controllers/devise_token_auth/sessions_controller_test.rb +0 -38
  63. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +2 -1
  64. data/test/dummy/app/active_record/confirmable_user.rb +11 -0
  65. data/test/dummy/app/{models → active_record}/lockable_user.rb +0 -0
  66. data/test/dummy/app/{models → active_record}/mang.rb +0 -0
  67. data/test/dummy/app/{models → active_record}/only_email_user.rb +0 -0
  68. data/test/dummy/app/{models → active_record}/scoped_user.rb +2 -2
  69. data/test/dummy/app/{models → active_record}/unconfirmable_user.rb +1 -2
  70. data/test/dummy/app/{models → active_record}/unregisterable_user.rb +3 -3
  71. data/test/dummy/app/active_record/user.rb +6 -0
  72. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +3 -3
  73. data/test/dummy/app/controllers/overrides/passwords_controller.rb +3 -3
  74. data/test/dummy/app/controllers/overrides/registrations_controller.rb +1 -1
  75. data/test/dummy/app/controllers/overrides/sessions_controller.rb +2 -2
  76. data/test/dummy/app/models/{user.rb → concerns/favorite_color.rb} +7 -8
  77. data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
  78. data/test/dummy/app/mongoid/lockable_user.rb +38 -0
  79. data/test/dummy/app/mongoid/mang.rb +46 -0
  80. data/test/dummy/app/mongoid/only_email_user.rb +33 -0
  81. data/test/dummy/app/mongoid/scoped_user.rb +50 -0
  82. data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
  83. data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
  84. data/test/dummy/app/mongoid/user.rb +49 -0
  85. data/test/dummy/config/application.rb +23 -1
  86. data/test/dummy/config/boot.rb +4 -0
  87. data/test/dummy/config/initializers/devise.rb +285 -0
  88. data/test/dummy/config/initializers/devise_token_auth.rb +35 -4
  89. data/test/dummy/config/initializers/figaro.rb +1 -1
  90. data/test/dummy/config/initializers/omniauth.rb +1 -0
  91. data/test/dummy/config/routes.rb +2 -0
  92. data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +0 -7
  93. data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +0 -7
  94. data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +0 -7
  95. data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +0 -7
  96. data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +0 -7
  97. data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +0 -7
  98. data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +0 -7
  99. data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
  100. data/test/dummy/db/schema.rb +26 -28
  101. data/test/factories/users.rb +3 -2
  102. data/test/lib/devise_token_auth/blacklist_test.rb +11 -0
  103. data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
  104. data/test/lib/devise_token_auth/url_test.rb +2 -2
  105. data/test/lib/generators/devise_token_auth/install_generator_test.rb +51 -31
  106. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +51 -31
  107. data/test/models/concerns/mongoid_support_test.rb +31 -0
  108. data/test/models/concerns/tokens_serialization_test.rb +70 -0
  109. data/test/models/confirmable_user_test.rb +35 -0
  110. data/test/models/only_email_user_test.rb +0 -8
  111. data/test/models/user_test.rb +1 -33
  112. data/test/test_helper.rb +13 -3
  113. metadata +129 -26
  114. data/config/initializers/devise.rb +0 -198
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 916c84285bb6b805d3a70a5adc8e86d83efb348e
4
- data.tar.gz: 38deed793c1466de04cd75ab4861627a5eb18447
2
+ SHA256:
3
+ metadata.gz: 395c104491ef2762e5c41f0b35af5f2421f8d24c99cc10145231d1cb2cab2d70
4
+ data.tar.gz: c637be9bc9c731f1b6218002925c0e558dbc62f2d6fb999fdd187d31d60e20c4
5
5
  SHA512:
6
- metadata.gz: 4b631710faf5afc08a2f0f0fc89a96e63c7fe07d0c7a985a19d3b5c0a18801401afe67525aa6e0a078195ca4d24c58b6a6651da667c2a7a7f40723d4974dc850
7
- data.tar.gz: 8707ad62656749fc88de275533456b93c17545f854e40f03920365c2ba9a11c170af44787118c4d8bc59ef593c5535ce8e09c512338c0de8af860c7e2500746f
6
+ metadata.gz: a1a184d38110e9157c941f1b5e2b8a0cdd7901702f12c7316a4ffba2b5af239455bddc9c288d8fbbd2c909aadfdfe388283c16abcce1814abf595cfe853e3c51
7
+ data.tar.gz: 7ac1939d622a50f46e9ce3943826b85e67e9457178bba79326c5656f4c8fbacc5205b44828aa4935be4c2c4dc713f68ab1d44b8d7485ced86fa90416769e1431
data/README.md CHANGED
@@ -19,7 +19,7 @@ Also, it maintains a session for each client/device, so you can have as many ses
19
19
 
20
20
  * Seamless integration with:
21
21
  * [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) for [AngularJS](https://github.com/angular/angular.js)
22
- * [Angular2-Token](https://github.com/neroniaky/angular2-token) for [Angular2](https://github.com/angular/angular)
22
+ * [Angular-Token](https://github.com/neroniaky/angular-token) for [Angular](https://github.com/angular/angular)
23
23
  * [redux-token-auth](https://github.com/kylecorbelli/redux-token-auth) for [React with Redux](https://github.com/reactjs/react-redux)
24
24
  * [jToker](https://github.com/lynndylanhurley/j-toker) for [jQuery](https://jquery.com/)
25
25
  * Oauth2 authentication using [OmniAuth](https://github.com/intridea/omniauth).
@@ -65,11 +65,13 @@ Please read the [issue template](https://github.com/lynndylanhurley/devise_token
65
65
 
66
66
  See our [Contribution Guidelines](https://github.com/lynndylanhurley/devise_token_auth/blob/master/.github/CONTRIBUTING.md). Feel free to submit pull requests, review pull requests, or review open issues. If you'd like to get in contact, [Zach Feldman](https://github.com/zachfeldman) has been wrangling this effort, you can reach him with his name @gmail. Further discussion of this in [this issue](https://github.com/lynndylanhurley/devise_token_auth/issues/969).
67
67
 
68
+ We have some bounties for some issues, [check them out](https://github.com/lynndylanhurley/devise_token_auth/issues?q=is%3Aopen+is%3Aissue+label%3Abounty)!
69
+
68
70
  ## Live Demos
69
71
 
70
72
  [Here is a demo](http://ng-token-auth-demo.herokuapp.com/) of this app running with the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module and [AngularJS](https://github.com/angular/angular.js).
71
73
 
72
- [Here is a demo](https://angular2-token.herokuapp.com) of this app running with the [Angular2-Token](https://github.com/neroniaky/angular2-token) service and [Angular2](https://github.com/angular/angular).
74
+ [Here is a demo](https://stackblitz.com/github/neroniaky/angular-token) of this app running with the [Angular-Token](https://github.com/neroniaky/angular-token) service and [Angular](https://github.com/angular/angular).
73
75
 
74
76
  [Here is a demo](https://j-toker-demo.herokuapp.com/) of this app using the [jToker](https://github.com/lynndylanhurley/j-toker) plugin and [React](http://facebook.github.io/react/).
75
77
 
@@ -3,7 +3,6 @@
3
3
  module DeviseTokenAuth
4
4
  class ApplicationController < DeviseController
5
5
  include DeviseTokenAuth::Concerns::SetUserByToken
6
- include DeviseTokenAuth::Concerns::ResourceFinder
7
6
 
8
7
  def resource_data(opts = {})
9
8
  response_data = opts[:resource_json] || @resource.as_json
@@ -17,8 +16,8 @@ module DeviseTokenAuth
17
16
 
18
17
  protected
19
18
 
20
- def blacklisted_redirect_url?
21
- DeviseTokenAuth.redirect_whitelist && !DeviseTokenAuth::Url.whitelisted?(@redirect_url)
19
+ def blacklisted_redirect_url?(redirect_url)
20
+ DeviseTokenAuth.redirect_whitelist && !DeviseTokenAuth::Url.whitelisted?(redirect_url)
22
21
  end
23
22
 
24
23
  def build_redirect_headers(access_token, client, redirect_header_options = {})
@@ -20,21 +20,20 @@ module DeviseTokenAuth::Concerns::ResourceFinder
20
20
  end
21
21
 
22
22
  def find_resource(field, value)
23
- # fix for mysql default case insensitivity
24
- q = "#{field.to_s} = ? AND provider='#{provider.to_s}'"
25
- if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
26
- q = 'BINARY ' + q
27
- end
28
-
29
- @resource = resource_class.where(q, value).first
23
+ @resource = if resource_class.try(:connection_config).try(:[], :adapter).try(:include?, 'mysql')
24
+ # fix for mysql default case insensitivity
25
+ resource_class.where("BINARY #{field} = ? AND provider= ?", value, provider).first
26
+ else
27
+ resource_class.dta_find_by(field => value, 'provider' => provider)
28
+ end
30
29
  end
31
30
 
32
31
  def resource_class(m = nil)
33
- if m
34
- mapping = Devise.mappings[m]
35
- else
36
- mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
37
- end
32
+ mapping = if m
33
+ Devise.mappings[m]
34
+ else
35
+ Devise.mappings[resource_name] || Devise.mappings.values.first
36
+ end
38
37
 
39
38
  mapping.to
40
39
  end
@@ -17,24 +17,11 @@ module DeviseTokenAuth::Concerns::SetUserByToken
17
17
  @used_auth_by_token = true
18
18
 
19
19
  # initialize instance variables
20
- @client_id ||= nil
20
+ @token = DeviseTokenAuth::TokenFactory.new
21
21
  @resource ||= nil
22
- @token ||= nil
23
22
  @is_batch_request ||= nil
24
23
  end
25
24
 
26
- def ensure_pristine_resource
27
- if @resource.changed?
28
- # Stash pending changes in the resource before reloading.
29
- changes = @resource.changes
30
- @resource.reload
31
- end
32
- yield
33
- ensure
34
- # Reapply pending changes
35
- @resource.assign_attributes(changes) if changes
36
- end
37
-
38
25
  # user auth
39
26
  def set_user_by_token(mapping = nil)
40
27
  # determine target authentication class
@@ -49,17 +36,18 @@ module DeviseTokenAuth::Concerns::SetUserByToken
49
36
  client_name = DeviseTokenAuth.headers_names[:'client']
50
37
 
51
38
  # parse header for values necessary for authentication
52
- uid = request.headers[uid_name] || params[uid_name]
53
- @token ||= request.headers[access_token_name] || params[access_token_name]
54
- @client_id ||= request.headers[client_name] || params[client_name]
39
+ uid = request.headers[uid_name] || params[uid_name]
40
+ @token = DeviseTokenAuth::TokenFactory.new unless @token
41
+ @token.token ||= request.headers[access_token_name] || params[access_token_name]
42
+ @token.client ||= request.headers[client_name] || params[client_name]
55
43
 
56
- # client_id isn't required, set to 'default' if absent
57
- @client_id ||= 'default'
44
+ # client isn't required, set to 'default' if absent
45
+ @token.client ||= 'default'
58
46
 
59
47
  # check for an existing user, authenticated via warden/devise, if enabled
60
48
  if DeviseTokenAuth.enable_standard_devise_support
61
49
  devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
62
- if devise_warden_user && devise_warden_user.tokens[@client_id].nil?
50
+ if devise_warden_user && devise_warden_user.tokens[@token.client].nil?
63
51
  @used_auth_by_token = false
64
52
  @resource = devise_warden_user
65
53
  # REVIEW: The following line _should_ be safe to remove;
@@ -71,46 +59,44 @@ module DeviseTokenAuth::Concerns::SetUserByToken
71
59
  # user has already been found and authenticated
72
60
  return @resource if @resource && @resource.is_a?(rc)
73
61
 
74
- # ensure we clear the client_id
75
- unless @token
76
- @client_id = nil
62
+ # ensure we clear the client
63
+ unless @token.present?
64
+ @token.client = nil
77
65
  return
78
66
  end
79
67
 
80
- return false unless @token
81
-
82
68
  # mitigate timing attacks by finding by uid instead of auth token
83
- user = uid && rc.find_by(uid: uid)
69
+ user = uid && rc.dta_find_by(uid: uid)
70
+ scope = rc.to_s.underscore.to_sym
84
71
 
85
- if user && user.valid_token?(@token, @client_id)
72
+ if user && user.valid_token?(@token.token, @token.client)
86
73
  # sign_in with bypass: true will be deprecated in the next version of Devise
87
74
  if respond_to?(:bypass_sign_in) && DeviseTokenAuth.bypass_sign_in
88
- bypass_sign_in(user, scope: :user)
75
+ bypass_sign_in(user, scope: scope)
89
76
  else
90
- sign_in(:user, user, store: false, event: :fetch, bypass: DeviseTokenAuth.bypass_sign_in)
77
+ sign_in(scope, user, store: false, event: :fetch, bypass: DeviseTokenAuth.bypass_sign_in)
91
78
  end
92
79
  return @resource = user
93
80
  else
94
81
  # zero all values previously set values
95
- @client_id = nil
82
+ @token.client = nil
96
83
  return @resource = nil
97
84
  end
98
85
  end
99
86
 
100
87
  def update_auth_header
101
88
  # cannot save object if model has invalid params
89
+ return unless @resource && @token.client
102
90
 
103
- return unless @resource && @client_id
104
-
105
- # Generate new client_id with existing authentication
106
- @client_id = nil unless @used_auth_by_token
91
+ # Generate new client with existing authentication
92
+ @token.client = nil unless @used_auth_by_token
107
93
 
108
94
  if @used_auth_by_token && !DeviseTokenAuth.change_headers_on_each_request
109
95
  # should not append auth header if @resource related token was
110
96
  # cleared by sign out in the meantime
111
- return if @resource.reload.tokens[@client_id].nil?
97
+ return if @resource.reload.tokens[@token.client].nil?
112
98
 
113
- auth_header = @resource.build_auth_header(@token, @client_id)
99
+ auth_header = @resource.build_auth_header(@token.token, @token.client)
114
100
 
115
101
  # update the response header
116
102
  response.headers.merge!(auth_header)
@@ -130,37 +116,35 @@ module DeviseTokenAuth::Concerns::SetUserByToken
130
116
  private
131
117
 
132
118
  def refresh_headers
133
- ensure_pristine_resource do
134
- # Lock the user record during any auth_header updates to ensure
135
- # we don't have write contention from multiple threads
136
- @resource.with_lock do
137
- # should not append auth header if @resource related token was
138
- # cleared by sign out in the meantime
139
- return if @used_auth_by_token && @resource.tokens[@client_id].nil?
140
-
141
- # update the response header
142
- response.headers.merge!(auth_header_from_batch_request)
143
- end # end lock
144
- end # end ensure_pristine_resource
119
+ # Lock the user record during any auth_header updates to ensure
120
+ # we don't have write contention from multiple threads
121
+ @resource.with_lock do
122
+ # should not append auth header if @resource related token was
123
+ # cleared by sign out in the meantime
124
+ return if @used_auth_by_token && @resource.tokens[@token.client].nil?
125
+
126
+ # update the response header
127
+ response.headers.merge!(auth_header_from_batch_request)
128
+ end # end lock
145
129
  end
146
130
 
147
- def is_batch_request?(user, client_id)
131
+ def is_batch_request?(user, client)
148
132
  !params[:unbatch] &&
149
- user.tokens[client_id] &&
150
- user.tokens[client_id]['updated_at'] &&
151
- Time.parse(user.tokens[client_id]['updated_at']) > @request_started_at - DeviseTokenAuth.batch_request_buffer_throttle
133
+ user.tokens[client] &&
134
+ user.tokens[client]['updated_at'] &&
135
+ user.tokens[client]['updated_at'].to_time > @request_started_at - DeviseTokenAuth.batch_request_buffer_throttle
152
136
  end
153
137
 
154
138
  def auth_header_from_batch_request
155
139
  # determine batch request status after request processing, in case
156
140
  # another processes has updated it during that processing
157
- @is_batch_request = is_batch_request?(@resource, @client_id)
141
+ @is_batch_request = is_batch_request?(@resource, @token.client)
158
142
 
159
143
  auth_header = {}
160
144
  # extend expiration of batch buffer to account for the duration of
161
145
  # this request
162
146
  if @is_batch_request
163
- auth_header = @resource.extend_batch_buffer(@token, @client_id)
147
+ auth_header = @resource.extend_batch_buffer(@token.token, @token.client)
164
148
 
165
149
  # Do not return token for batch requests to avoid invalidated
166
150
  # tokens returned to the client in case of race conditions.
@@ -171,7 +155,7 @@ module DeviseTokenAuth::Concerns::SetUserByToken
171
155
  auth_header[DeviseTokenAuth.headers_names[:"expiry"]] = ' '
172
156
  else
173
157
  # update Authorization response header with new token
174
- auth_header = @resource.create_new_auth_token(@client_id)
158
+ auth_header = @resource.create_new_auth_token(@token.client)
175
159
  end
176
160
  auth_header
177
161
  end
@@ -2,38 +2,81 @@
2
2
 
3
3
  module DeviseTokenAuth
4
4
  class ConfirmationsController < DeviseTokenAuth::ApplicationController
5
- def show
6
- @resource = resource_class.confirm_by_token(params[:confirmation_token])
7
-
8
- if @resource && @resource.id
9
- expiry = nil
10
- if defined?(@resource.sign_in_count) && @resource.sign_in_count > 0
11
- expiry = (Time.zone.now + 1.second).to_i
12
- end
13
-
14
- client_id, token = @resource.create_token expiry: expiry
15
5
 
16
- sign_in(@resource)
17
- @resource.save!
6
+ def show
7
+ @resource = resource_class.confirm_by_token(resource_params[:confirmation_token])
18
8
 
9
+ if @resource.errors.empty?
19
10
  yield @resource if block_given?
20
11
 
21
12
  redirect_header_options = { account_confirmation_success: true }
22
- redirect_headers = build_redirect_headers(token,
23
- client_id,
24
- redirect_header_options)
25
13
 
26
- # give redirect value from params priority
27
- @redirect_url = params[:redirect_url]
14
+ if signed_in?(resource_name)
15
+ token = signed_in_resource.create_token
16
+ signed_in_resource.save!
28
17
 
29
- # fall back to default value if provided
30
- @redirect_url ||= DeviseTokenAuth.default_confirm_success_url
18
+ redirect_headers = build_redirect_headers(token.token,
19
+ token.client,
20
+ redirect_header_options)
31
21
 
22
+ redirect_to_link = signed_in_resource.build_auth_url(redirect_url, redirect_headers)
23
+ else
24
+ redirect_to_link = DeviseTokenAuth::Url.generate(redirect_url, redirect_header_options)
25
+ end
32
26
 
33
- redirect_to(@resource.build_auth_url(@redirect_url, redirect_headers))
27
+ redirect_to(redirect_to_link)
34
28
  else
35
29
  raise ActionController::RoutingError, 'Not Found'
36
30
  end
37
31
  end
32
+
33
+ def create
34
+ return render_create_error_missing_email if resource_params[:email].blank?
35
+
36
+ @email = get_case_insensitive_field_from_resource_params(:email)
37
+
38
+ @resource = resource_class.dta_find_by(uid: @email, provider: provider)
39
+
40
+ return render_not_found_error unless @resource
41
+
42
+ @resource.send_confirmation_instructions({
43
+ redirect_url: redirect_url,
44
+ client_config: resource_params[:config_name]
45
+ })
46
+
47
+ return render_create_success
48
+ end
49
+
50
+ protected
51
+
52
+ def render_create_error_missing_email
53
+ render_error(401, I18n.t('devise_token_auth.confirmations.missing_email'))
54
+ end
55
+
56
+ def render_create_success
57
+ render json: {
58
+ success: true,
59
+ message: I18n.t('devise_token_auth.confirmations.sended', email: @email)
60
+ }
61
+ end
62
+
63
+ def render_not_found_error
64
+ render_error(404, I18n.t('devise_token_auth.confirmations.user_not_found', email: @email))
65
+ end
66
+
67
+ private
68
+
69
+ def resource_params
70
+ params.permit(:email, :confirmation_token, :config_name)
71
+ end
72
+
73
+ # give redirect value from params priority or fall back to default value if provided
74
+ def redirect_url
75
+ params.fetch(
76
+ :redirect_url,
77
+ DeviseTokenAuth.default_confirm_success_url
78
+ )
79
+ end
80
+
38
81
  end
39
82
  end
@@ -3,6 +3,9 @@
3
3
  module DeviseTokenAuth
4
4
  class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
5
5
  attr_reader :auth_params
6
+
7
+ before_action :validate_auth_origin_url_param
8
+
6
9
  skip_before_action :set_user_by_token, raise: false
7
10
  skip_after_action :update_auth_header
8
11
 
@@ -12,11 +15,8 @@ module DeviseTokenAuth
12
15
 
13
16
  # derive target redirect route from 'resource_class' param, which was set
14
17
  # before authentication.
15
- devise_mapping = [request.env['omniauth.params']['namespace_name'],
16
- request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
17
- path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
18
- klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
19
- redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
18
+ devise_mapping = get_devise_mapping
19
+ redirect_route = get_redirect_route(devise_mapping)
20
20
 
21
21
  # preserve omniauth info for success route. ignore 'extra' in twitter
22
22
  # auth response to avoid CookieOverflow.
@@ -26,6 +26,34 @@ module DeviseTokenAuth
26
26
  redirect_to redirect_route
27
27
  end
28
28
 
29
+ def get_redirect_route(devise_mapping)
30
+ path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
31
+ klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
32
+ redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
33
+ end
34
+
35
+ def get_devise_mapping
36
+ # derive target redirect route from 'resource_class' param, which was set
37
+ # before authentication.
38
+ devise_mapping = [request.env['omniauth.params']['namespace_name'],
39
+ request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
40
+ rescue NoMethodError => err
41
+ default_devise_mapping
42
+ end
43
+
44
+ # This method will only be called if `get_devise_mapping` cannot
45
+ # find the mapping in `omniauth.params`.
46
+ #
47
+ # One example use-case here is for IDP-initiated SAML login. In that
48
+ # case, there will have been no initial request in which to save
49
+ # the devise mapping. If you are in a situation like that, and
50
+ # your app allows for you to determine somehow what the devise
51
+ # mapping should be (because, for example, it is always the same),
52
+ # then you can handle it by overriding this method.
53
+ def default_devise_mapping
54
+ raise NotImplementedError.new('no default_devise_mapping set')
55
+ end
56
+
29
57
  def omniauth_success
30
58
  get_resource_from_auth_hash
31
59
  set_token_on_resource
@@ -50,6 +78,11 @@ module DeviseTokenAuth
50
78
  render_data_or_redirect('authFailure', error: @error)
51
79
  end
52
80
 
81
+ def validate_auth_origin_url_param
82
+ return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
83
+ end
84
+
85
+
53
86
  protected
54
87
 
55
88
  # this will be determined differently depending on the action that calls
@@ -79,7 +112,8 @@ module DeviseTokenAuth
79
112
 
80
113
  # break out provider attribute assignment for easy method extension
81
114
  def assign_provider_attrs(user, auth_hash)
82
- attrs = auth_hash['info'].slice(*user.attributes.keys)
115
+ attrs = auth_hash['info'].to_hash
116
+ attrs = attrs.slice(*user.attribute_names)
83
117
  user.assign_attributes(attrs)
84
118
  end
85
119
 
@@ -112,10 +146,18 @@ module DeviseTokenAuth
112
146
  omniauth_params['omniauth_window_type']
113
147
  end
114
148
 
115
- def auth_origin_url
149
+ def unsafe_auth_origin_url
116
150
  omniauth_params['auth_origin_url'] || omniauth_params['origin']
117
151
  end
118
152
 
153
+
154
+ def auth_origin_url
155
+ if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
156
+ return nil
157
+ end
158
+ return unsafe_auth_origin_url
159
+ end
160
+
119
161
  # in the success case, omniauth_window_type is in the omniauth_params.
120
162
  # in the failure case, it is in a query param. See monkey patch above
121
163
  def omniauth_window_type
@@ -136,16 +178,6 @@ module DeviseTokenAuth
136
178
  true
137
179
  end
138
180
 
139
- # necessary for access to devise_parameter_sanitizers
140
- def devise_mapping
141
- if omniauth_params
142
- Devise.mappings[[omniauth_params['namespace_name'],
143
- omniauth_params['resource_class'].underscore].compact.join('_').to_sym]
144
- else
145
- request.env['devise.mapping']
146
- end
147
- end
148
-
149
181
  def set_random_password
150
182
  # set crazy password for new oauth users. this is only used to prevent
151
183
  # access via email sign-in.
@@ -156,11 +188,11 @@ module DeviseTokenAuth
156
188
 
157
189
  def create_auth_params
158
190
  @auth_params = {
159
- auth_token: @token,
160
- client_id: @client_id,
161
- uid: @resource.uid,
162
- expiry: @expiry,
163
- config: @config
191
+ auth_token: @token.token,
192
+ client_id: @token.client,
193
+ uid: @resource.uid,
194
+ expiry: @token.expiry,
195
+ config: @config
164
196
  }
165
197
  @auth_params.merge!(oauth_registration: true) if @oauth_registration
166
198
  @auth_params
@@ -168,11 +200,16 @@ module DeviseTokenAuth
168
200
 
169
201
  def set_token_on_resource
170
202
  @config = omniauth_params['config_name']
171
- @client_id, @token, @expiry = @resource.create_token
203
+ @token = @resource.create_token
204
+ end
205
+
206
+ def render_error_not_allowed_auth_origin_url
207
+ message = I18n.t('devise_token_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
208
+ render_data_or_redirect('authFailure', error: message)
172
209
  end
173
210
 
174
211
  def render_data(message, data)
175
- @data = data.merge(message: message)
212
+ @data = data.merge(message: ActionController::Base.helpers.sanitize(message))
176
213
  render layout: nil, template: 'devise_token_auth/omniauth_external_window'
177
214
  end
178
215
 
@@ -209,11 +246,20 @@ module DeviseTokenAuth
209
246
  <html>
210
247
  <head></head>
211
248
  <body>
212
- #{text}
249
+ #{ActionController::Base.helpers.sanitize(text)}
213
250
  </body>
214
251
  </html>)
215
252
  end
216
253
 
254
+ def handle_new_resource
255
+ @oauth_registration = true
256
+ set_random_password
257
+ end
258
+
259
+ def assign_whitelisted_params?
260
+ true
261
+ end
262
+
217
263
  def get_resource_from_auth_hash
218
264
  # find or create user by provider and provider uid
219
265
  @resource = resource_class.where(
@@ -222,18 +268,20 @@ module DeviseTokenAuth
222
268
  ).first_or_initialize
223
269
 
224
270
  if @resource.new_record?
225
- @oauth_registration = true
226
- set_random_password
271
+ handle_new_resource
227
272
  end
228
273
 
229
274
  # sync user info with provider, update/generate auth token
230
275
  assign_provider_attrs(@resource, auth_hash)
231
276
 
232
277
  # assign any additional (whitelisted) attributes
233
- extra_params = whitelisted_params
234
- @resource.assign_attributes(extra_params) if extra_params
278
+ if assign_whitelisted_params?
279
+ extra_params = whitelisted_params
280
+ @resource.assign_attributes(extra_params) if extra_params
281
+ end
235
282
 
236
283
  @resource
237
284
  end
238
285
  end
286
+
239
287
  end