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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/app/controllers/devise_token_auth/application_controller.rb +18 -2
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +5 -0
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +51 -38
- data/app/controllers/devise_token_auth/confirmations_controller.rb +3 -11
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +3 -14
- data/app/controllers/devise_token_auth/passwords_controller.rb +34 -55
- data/app/controllers/devise_token_auth/registrations_controller.rb +34 -46
- data/app/controllers/devise_token_auth/sessions_controller.rb +15 -24
- data/app/controllers/devise_token_auth/token_validations_controller.rb +2 -5
- data/app/controllers/devise_token_auth/unlocks_controller.rb +11 -27
- data/app/models/devise_token_auth/concerns/user.rb +60 -85
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +2 -1
- data/config/initializers/devise.rb +4 -4
- data/config/locales/pl.yml +10 -10
- data/config/locales/uk.yml +59 -0
- data/config/locales/vi.yml +50 -0
- data/lib/devise_token_auth/controllers/helpers.rb +1 -1
- data/lib/devise_token_auth/engine.rb +3 -1
- data/lib/devise_token_auth/version.rb +1 -1
- data/lib/generators/devise_token_auth/templates/user.rb +3 -3
- data/test/controllers/demo_user_controller_test.rb +56 -0
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +10 -6
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +103 -1
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +24 -1
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +1 -11
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +1 -9
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +1 -8
- data/test/models/user_test.rb +11 -1
- data/test/test_helper.rb +9 -0
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d76ab61068684b190f5e9ced53aaf80a61de83ac
|
4
|
+
data.tar.gz: 0617315c78c9bf77b2c35385585453699cbee307
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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
|
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
|
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?
|
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:
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
8
|
-
|
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.
|
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
|
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
|
-
@
|
181
|
-
|
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
|
-
|
45
|
+
render_create_error @resource.errors
|
49
46
|
end
|
50
47
|
else
|
51
|
-
|
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 =
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
128
|
+
response = {
|
152
129
|
status: 'error',
|
153
|
-
data: resource_data
|
154
|
-
|
155
|
-
|
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:
|
169
|
-
}, 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
|
-
|
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
|
-
|
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
|
-
|
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, :
|
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
|