devise_token_auth 0.1.43.beta1 → 0.1.43

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise_token_auth might be problematic. Click here for more details.

Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/Rakefile +1 -0
  4. data/app/controllers/devise_token_auth/application_controller.rb +18 -2
  5. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +5 -0
  6. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +51 -38
  7. data/app/controllers/devise_token_auth/confirmations_controller.rb +3 -11
  8. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +3 -14
  9. data/app/controllers/devise_token_auth/passwords_controller.rb +34 -55
  10. data/app/controllers/devise_token_auth/registrations_controller.rb +34 -46
  11. data/app/controllers/devise_token_auth/sessions_controller.rb +15 -24
  12. data/app/controllers/devise_token_auth/token_validations_controller.rb +2 -5
  13. data/app/controllers/devise_token_auth/unlocks_controller.rb +11 -27
  14. data/app/models/devise_token_auth/concerns/user.rb +60 -85
  15. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +2 -1
  16. data/config/initializers/devise.rb +4 -4
  17. data/config/locales/pl.yml +10 -10
  18. data/config/locales/uk.yml +59 -0
  19. data/config/locales/vi.yml +50 -0
  20. data/lib/devise_token_auth/controllers/helpers.rb +1 -1
  21. data/lib/devise_token_auth/engine.rb +3 -1
  22. data/lib/devise_token_auth/version.rb +1 -1
  23. data/lib/generators/devise_token_auth/templates/user.rb +3 -3
  24. data/test/controllers/demo_user_controller_test.rb +56 -0
  25. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +10 -6
  26. data/test/controllers/devise_token_auth/passwords_controller_test.rb +103 -1
  27. data/test/controllers/devise_token_auth/sessions_controller_test.rb +24 -1
  28. data/test/dummy/app/controllers/overrides/confirmations_controller.rb +1 -11
  29. data/test/dummy/app/controllers/overrides/passwords_controller.rb +1 -9
  30. data/test/dummy/app/controllers/overrides/sessions_controller.rb +1 -8
  31. data/test/models/user_test.rb +11 -1
  32. data/test/test_helper.rb +9 -0
  33. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49f4bcdd5d7a311223110f453bd4575fe89768e0
4
- data.tar.gz: f6ccefa63103a9da877b9872a2f23aa26ce5bbf0
3
+ metadata.gz: d76ab61068684b190f5e9ced53aaf80a61de83ac
4
+ data.tar.gz: 0617315c78c9bf77b2c35385585453699cbee307
5
5
  SHA512:
6
- metadata.gz: bd75219ef40a2a64ed0cba0f57631b2d097c7d0187ae596d3db16b9b59f38fa8ae47edc7c87627ff65c4f3e5f1c0c626b610cb6d585c310f37a023709838dd0d
7
- data.tar.gz: 66658c65532b7271c542302f75c2798db7f6bd425fc642ad70f67ca7f9d3dd6869ca0199df6d911fa2eced7a6fdba8997f30775193143b40634d6959e38c6945
6
+ metadata.gz: 4f13d725883dca5733e9c93365ccd59ff36ba66e4e14d8804128c9a5619b988c78fb5188d5fda88b381e68d293f85b477403096d9a34c561d366cd7ced2b1400
7
+ data.tar.gz: 3157a019543604074cfcd411c1742ac3731356f080303d6176106f7f2daf998b30fbc7a0236da09181252017ebcbd8d5aa817ec6b351459bb70878caf6cfcaba
data/README.md CHANGED
@@ -11,6 +11,7 @@ See our [Contribution Guidelines](https://github.com/lynndylanhurley/devise_toke
11
11
  [![Code Climate](http://img.shields.io/codeclimate/github/lynndylanhurley/devise_token_auth.svg)](https://codeclimate.com/github/lynndylanhurley/devise_token_auth)
12
12
  [![Test Coverage](http://img.shields.io/codeclimate/coverage/github/lynndylanhurley/devise_token_auth.svg)](https://codeclimate.com/github/lynndylanhurley/devise_token_auth)
13
13
  [![Dependency Status](https://gemnasium.com/lynndylanhurley/devise_token_auth.svg)](https://gemnasium.com/lynndylanhurley/devise_token_auth)
14
+ [![Downloads](https://img.shields.io/gem/dt/devise_token_auth.svg)](https://rubygems.org/gems/devise_token_auth)
14
15
 
15
16
  ## Simple, secure token based authentication for Rails.
16
17
 
@@ -21,6 +22,7 @@ This gem provides the following features:
21
22
  * Seamless integration with:
22
23
  * [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) for [AngularJS](https://github.com/angular/angular.js)
23
24
  * [Angular2-Token](https://github.com/neroniaky/angular2-token) for [Angular2](https://github.com/angular/angular)
25
+ * [redux-token-auth](https://github.com/kylecorbelli/redux-token-auth) for [React with Redux](https://github.com/reactjs/react-redux)
24
26
  * [jToker](https://github.com/lynndylanhurley/j-toker) for [jQuery](https://jquery.com/)
25
27
  * Oauth2 authentication using [OmniAuth](https://github.com/intridea/omniauth).
26
28
  * Email authentication using [Devise](https://github.com/plataformatec/devise), including:
@@ -69,6 +71,7 @@ Please read the [issue reporting guidelines](#issue-reporting) before posting is
69
71
  * [Testing](#testing)
70
72
  * [Issue Reporting Guidelines](#issue-reporting)
71
73
  * [FAQ](#faq)
74
+ * [Can I use this gem alongside standard Devise?](#can-i-use-this-gem-alongside-standard-devise)
72
75
  * [Conceptual Diagrams](#conceptual)
73
76
  * [Token Management](#about-token-management)
74
77
  * [Batch Requests](#about-batch-requests)
@@ -179,6 +182,7 @@ The following settings are available for configuration in `config/initializers/d
179
182
  | **`enable_standard_devise_support`** | `false` | By default, only Bearer Token authentication is implemented out of the box. If, however, you wish to integrate with legacy Devise authentication, you can do so by enabling this flag. NOTE: This feature is highly experimental! |
180
183
  | **`remove_tokens_after_password_reset`** | `false` | By default, old tokens are not invalidated when password is changed. Enable this option if you want to make passwords updates to logout other devices. |
181
184
  | **`default_callbacks`** | `true` | By default User model will include the `DeviseTokenAuth::Concerns::UserOmniauthCallbacks` concern, which has `email`, `uid` validations & `uid` synchronization callbacks. |
185
+ | **`bypass_sign_in`** | `true` | By default DeviseTokenAuth will not check user's `#active_for_authentication?` which includes confirmation check on each call (it will do it only on sign in). If you want it to be validated on each request (for example, to be able to deactivate logged in users on the fly), set it to false. |
182
186
 
183
187
 
184
188
  Additionally, you can configure other aspects of devise by manually creating the traditional devise.rb file at `config/initializers/devise.rb`. Here are some examples of what you can do in this file:
data/Rakefile CHANGED
@@ -28,6 +28,7 @@ Rake::TestTask.new(:test) do |t|
28
28
  t.libs << 'test'
29
29
  t.pattern = 'test/**/*_test.rb'
30
30
  t.verbose = false
31
+ t.warning = false
31
32
  end
32
33
 
33
34
 
@@ -5,7 +5,7 @@ module DeviseTokenAuth
5
5
 
6
6
  def resource_data(opts={})
7
7
  response_data = opts[:resource_json] || @resource.as_json
8
- if is_json_api
8
+ if json_api?
9
9
  response_data['type'] = @resource.class.name.parameterize
10
10
  end
11
11
  response_data
@@ -48,7 +48,7 @@ module DeviseTokenAuth
48
48
  mapping.to
49
49
  end
50
50
 
51
- def is_json_api
51
+ def json_api?
52
52
  return false unless defined?(ActiveModel::Serializer)
53
53
  return ActiveModel::Serializer.setup do |config|
54
54
  config.adapter == :json_api
@@ -56,5 +56,21 @@ module DeviseTokenAuth
56
56
  return ActiveModelSerializers.config.adapter == :json_api
57
57
  end
58
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
59
75
  end
60
76
  end
@@ -9,6 +9,11 @@ module DeviseTokenAuth::Concerns::ResourceFinder
9
9
  if resource_class.case_insensitive_keys.include?(field.to_sym)
10
10
  q_value.downcase!
11
11
  end
12
+
13
+ if resource_class.strip_whitespace_keys.include?(field.to_sym)
14
+ q_value.strip!
15
+ end
16
+
12
17
  q_value
13
18
  end
14
19
 
@@ -21,6 +21,18 @@ module DeviseTokenAuth::Concerns::SetUserByToken
21
21
  @is_batch_request = nil
22
22
  end
23
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
+
24
36
  # user auth
25
37
  def set_user_by_token(mapping=nil)
26
38
  # determine target authentication class
@@ -68,10 +80,10 @@ module DeviseTokenAuth::Concerns::SetUserByToken
68
80
 
69
81
  if user && user.valid_token?(@token, @client_id)
70
82
  # sign_in with bypass: true will be deprecated in the next version of Devise
71
- if self.respond_to? :bypass_sign_in
83
+ if self.respond_to?(:bypass_sign_in) && DeviseTokenAuth.bypass_sign_in
72
84
  bypass_sign_in(user, scope: :user)
73
85
  else
74
- sign_in(:user, user, store: false, bypass: true)
86
+ sign_in(:user, user, store: false, event: :fetch, bypass: DeviseTokenAuth.bypass_sign_in)
75
87
  end
76
88
  return @resource = user
77
89
  else
@@ -100,42 +112,43 @@ module DeviseTokenAuth::Concerns::SetUserByToken
100
112
 
101
113
  else
102
114
 
103
- # Lock the user record during any auth_header updates to ensure
104
- # we don't have write contention from multiple threads
105
- @resource.with_lock do
106
- # should not append auth header if @resource related token was
107
- # cleared by sign out in the meantime
108
- return if @used_auth_by_token && @resource.tokens[@client_id].nil?
109
-
110
- # determine batch request status after request processing, in case
111
- # another processes has updated it during that processing
112
- @is_batch_request = is_batch_request?(@resource, @client_id)
113
-
114
- auth_header = {}
115
-
116
- # extend expiration of batch buffer to account for the duration of
117
- # this request
118
- if @is_batch_request
119
- auth_header = @resource.extend_batch_buffer(@token, @client_id)
120
-
121
- # Do not return token for batch requests to avoid invalidated
122
- # tokens returned to the client in case of race conditions.
123
- # Use a blank string for the header to still be present and
124
- # being passed in a XHR response in case of
125
- # 304 Not Modified responses.
126
- auth_header[DeviseTokenAuth.headers_names[:"access-token"]] = ' '
127
- auth_header[DeviseTokenAuth.headers_names[:"expiry"]] = ' '
128
-
129
- # update Authorization response header with new token
130
- else
131
- auth_header = @resource.create_new_auth_token(@client_id)
132
- end
133
-
134
- # update the response header
135
- response.headers.merge!(auth_header)
136
-
137
- end # end lock
138
-
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
139
152
  end
140
153
 
141
154
  end
@@ -4,20 +4,12 @@ module DeviseTokenAuth
4
4
  @resource = resource_class.confirm_by_token(params[:confirmation_token])
5
5
 
6
6
  if @resource && @resource.id
7
- # create client id
8
- client_id = SecureRandom.urlsafe_base64(nil, false)
9
- token = SecureRandom.urlsafe_base64(nil, false)
10
- token_hash = BCrypt::Password.create(token)
11
- expiry = (Time.now + @resource.token_lifespan).to_i
12
-
13
- if @resource.sign_in_count > 0
7
+ expiry = nil
8
+ if defined?(@resource.sign_in_count) && @resource.sign_in_count > 0
14
9
  expiry = (Time.now + 1.second).to_i
15
10
  end
16
11
 
17
- @resource.tokens[client_id] = {
18
- token: token_hash,
19
- expiry: expiry
20
- }
12
+ client_id, token = @resource.create_token expiry: expiry
21
13
 
22
14
  sign_in(@resource)
23
15
  @resource.save!
@@ -27,11 +27,10 @@ module DeviseTokenAuth
27
27
 
28
28
  def omniauth_success
29
29
  get_resource_from_auth_hash
30
- create_token_info
31
30
  set_token_on_resource
32
31
  create_auth_params
33
32
 
34
- if resource_class.devise_modules.include?(:confirmable)
33
+ if confirmable_enabled?
35
34
  # don't send confirmation email!!!
36
35
  @resource.skip_confirmation!
37
36
  end
@@ -156,14 +155,6 @@ module DeviseTokenAuth
156
155
  @resource.password_confirmation = p
157
156
  end
158
157
 
159
- def create_token_info
160
- # create token info
161
- @client_id = SecureRandom.urlsafe_base64(nil, false)
162
- @token = SecureRandom.urlsafe_base64(nil, false)
163
- @expiry = (Time.now + @resource.token_lifespan).to_i
164
- @config = omniauth_params['config_name']
165
- end
166
-
167
158
  def create_auth_params
168
159
  @auth_params = {
169
160
  auth_token: @token,
@@ -177,10 +168,8 @@ module DeviseTokenAuth
177
168
  end
178
169
 
179
170
  def set_token_on_resource
180
- @resource.tokens[@client_id] = {
181
- token: BCrypt::Password.create(@token),
182
- expiry: @expiry
183
- }
171
+ @config = omniauth_params['config_name']
172
+ @client_id, @token, @expiry = @resource.create_token
184
173
  end
185
174
 
186
175
  def render_data(message, data)
@@ -30,9 +30,6 @@ module DeviseTokenAuth
30
30
  @email = get_case_insensitive_field_from_resource_params(:email)
31
31
  @resource = find_resource(:uid, @email)
32
32
 
33
- @errors = nil
34
- @error_status = 400
35
-
36
33
  if @resource
37
34
  yield @resource if block_given?
38
35
  @resource.send_reset_password_instructions({
@@ -45,41 +42,26 @@ module DeviseTokenAuth
45
42
  if @resource.errors.empty?
46
43
  return render_create_success
47
44
  else
48
- @errors = @resource.errors
45
+ render_create_error @resource.errors
49
46
  end
50
47
  else
51
- @errors = [I18n.t("devise_token_auth.passwords.user_not_found", email: @email)]
52
- @error_status = 404
53
- end
54
-
55
- if @errors
56
- return render_create_error
48
+ render_not_found_error
57
49
  end
58
50
  end
59
51
 
60
52
  # this is where users arrive after visiting the password reset confirmation link
61
53
  def edit
62
54
  # if a user is not found, return nil
63
- @resource = resource_class.with_reset_password_token(
64
- resource_params[:reset_password_token]
65
- )
55
+ @resource = with_reset_password_token(resource_params[:reset_password_token])
66
56
 
67
- if @resource
68
- client_id = SecureRandom.urlsafe_base64(nil, false)
69
- token = SecureRandom.urlsafe_base64(nil, false)
70
- token_hash = BCrypt::Password.create(token)
71
- expiry = (Time.now + @resource.token_lifespan).to_i
72
-
73
- @resource.tokens[client_id] = {
74
- token: token_hash,
75
- expiry: expiry
76
- }
57
+ if @resource && @resource.reset_password_period_valid?
58
+ client_id, token = @resource.create_token
77
59
 
78
60
  # ensure that user is confirmed
79
- @resource.skip_confirmation! if @resource.devise_modules.include?(:confirmable) && !@resource.confirmed_at
61
+ @resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
80
62
 
81
63
  # allow user to change password once without current_password
82
- @resource.allow_password_change = true;
64
+ @resource.allow_password_change = true if recoverable_enabled?
83
65
 
84
66
  @resource.save!
85
67
 
@@ -113,7 +95,7 @@ module DeviseTokenAuth
113
95
  end
114
96
 
115
97
  if @resource.send(resource_update_method, password_resource_params)
116
- @resource.allow_password_change = false
98
+ @resource.allow_password_change = false if recoverable_enabled?
117
99
  @resource.save!
118
100
 
119
101
  yield @resource if block_given?
@@ -126,7 +108,8 @@ module DeviseTokenAuth
126
108
  protected
127
109
 
128
110
  def resource_update_method
129
- if DeviseTokenAuth.check_current_password_before_update == false or @resource.allow_password_change == true
111
+ allow_password_change = recoverable_enabled? && @resource.allow_password_change == true
112
+ if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
130
113
  "update_attributes"
131
114
  else
132
115
  "update_with_password"
@@ -134,25 +117,20 @@ module DeviseTokenAuth
134
117
  end
135
118
 
136
119
  def render_create_error_missing_email
137
- render json: {
138
- success: false,
139
- errors: [I18n.t("devise_token_auth.passwords.missing_email")]
140
- }, status: 401
120
+ render_error(401, I18n.t("devise_token_auth.passwords.missing_email"))
141
121
  end
142
122
 
143
123
  def render_create_error_missing_redirect_url
144
- render json: {
145
- success: false,
146
- errors: [I18n.t("devise_token_auth.passwords.missing_redirect_url")]
147
- }, status: 401
124
+ render_error(401, I18n.t("devise_token_auth.passwords.missing_redirect_url"))
148
125
  end
149
126
 
150
127
  def render_create_error_not_allowed_redirect_url
151
- render json: {
128
+ response = {
152
129
  status: 'error',
153
- data: resource_data,
154
- errors: [I18n.t("devise_token_auth.passwords.not_allowed_redirect_url", redirect_url: @redirect_url)]
155
- }, status: 422
130
+ data: resource_data
131
+ }
132
+ message = I18n.t("devise_token_auth.passwords.not_allowed_redirect_url", redirect_url: @redirect_url)
133
+ render_error(422, message, response)
156
134
  end
157
135
 
158
136
  def render_create_success
@@ -162,11 +140,11 @@ module DeviseTokenAuth
162
140
  }
163
141
  end
164
142
 
165
- def render_create_error
143
+ def render_create_error(errors)
166
144
  render json: {
167
145
  success: false,
168
- errors: @errors,
169
- }, status: @error_status
146
+ errors: errors,
147
+ }, status: 400
170
148
  end
171
149
 
172
150
  def render_edit_error
@@ -174,24 +152,15 @@ module DeviseTokenAuth
174
152
  end
175
153
 
176
154
  def render_update_error_unauthorized
177
- render json: {
178
- success: false,
179
- errors: ['Unauthorized']
180
- }, status: 401
155
+ render_error(401, 'Unauthorized')
181
156
  end
182
157
 
183
158
  def render_update_error_password_not_required
184
- render json: {
185
- success: false,
186
- errors: [I18n.t("devise_token_auth.passwords.password_not_required", provider: @resource.provider.humanize)]
187
- }, status: 422
159
+ render_error(422, I18n.t("devise_token_auth.passwords.password_not_required", provider: @resource.provider.humanize))
188
160
  end
189
161
 
190
162
  def render_update_error_missing_password
191
- render json: {
192
- success: false,
193
- errors: [I18n.t("devise_token_auth.passwords.missing_passwords")]
194
- }, status: 422
163
+ render_error(422, I18n.t("devise_token_auth.passwords.missing_passwords"))
195
164
  end
196
165
 
197
166
  def render_update_success
@@ -212,12 +181,22 @@ module DeviseTokenAuth
212
181
  private
213
182
 
214
183
  def resource_params
215
- params.permit(:email, :password, :password_confirmation, :current_password, :reset_password_token, :redirect_url, :config)
184
+ params.permit(:email, :reset_password_token)
216
185
  end
217
186
 
218
187
  def password_resource_params
219
188
  params.permit(*params_for_resource(:account_update))
220
189
  end
221
190
 
191
+ def with_reset_password_token token
192
+ recoverable = resource_class.with_reset_password_token(token)
193
+
194
+ recoverable.reset_password_token = token if recoverable && recoverable.reset_password_token.present?
195
+ recoverable
196
+ end
197
+
198
+ def render_not_found_error
199
+ render_error(404, I18n.t("devise_token_auth.passwords.user_not_found", email: @email))
200
+ end
222
201
  end
223
202
  end