devise_token_auth 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/devise_token_auth/application_controller.rb +17 -0
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +14 -1
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +45 -8
- data/app/controllers/devise_token_auth/confirmations_controller.rb +8 -4
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +8 -4
- data/app/controllers/devise_token_auth/passwords_controller.rb +10 -2
- data/app/controllers/devise_token_auth/sessions_controller.rb +21 -3
- data/app/controllers/devise_token_auth/unlocks_controller.rb +6 -2
- data/app/models/devise_token_auth/concerns/active_record_support.rb +0 -2
- data/app/models/devise_token_auth/concerns/confirmable_support.rb +2 -1
- data/app/models/devise_token_auth/concerns/tokens_serialization.rb +16 -4
- data/app/models/devise_token_auth/concerns/user.rb +31 -15
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +12 -5
- data/app/validators/devise_token_auth_email_validator.rb +10 -2
- data/app/views/devise_token_auth/omniauth_external_window.html.erb +1 -1
- data/config/locales/en.yml +3 -0
- data/config/locales/ja.yml +12 -0
- data/lib/devise_token_auth/blacklist.rb +5 -1
- data/lib/devise_token_auth/controllers/helpers.rb +5 -9
- data/lib/devise_token_auth/engine.rb +11 -2
- data/lib/devise_token_auth/rails/routes.rb +17 -12
- data/lib/devise_token_auth/version.rb +1 -1
- data/lib/generators/devise_token_auth/install_generator.rb +1 -1
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +3 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +1 -1
- data/test/controllers/demo_mang_controller_test.rb +37 -8
- data/test/controllers/demo_user_controller_test.rb +37 -8
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +100 -19
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +2 -2
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +73 -21
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +28 -15
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +39 -10
- data/test/controllers/devise_token_auth/token_validations_controller_test.rb +41 -1
- data/test/controllers/devise_token_auth/unlocks_controller_test.rb +44 -5
- data/test/controllers/overrides/confirmations_controller_test.rb +1 -1
- data/test/dummy/app/views/layouts/application.html.erb +0 -2
- data/test/dummy/config/application.rb +0 -1
- data/test/dummy/config/environments/development.rb +0 -10
- data/test/dummy/config/environments/production.rb +0 -16
- data/test/dummy/db/schema.rb +5 -5
- data/test/dummy/tmp/generators/app/models/user.rb +11 -0
- data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
- data/test/dummy/tmp/generators/db/migrate/20220822003050_devise_token_auth_create_users.rb +49 -0
- data/test/lib/devise_token_auth/blacklist_test.rb +11 -3
- data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
- data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +1 -1
- data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +1 -1
- data/test/models/concerns/tokens_serialization_test.rb +39 -5
- data/test/models/user_test.rb +22 -0
- data/test/test_helper.rb +35 -4
- metadata +16 -26
- data/test/dummy/config/initializers/assets.rb +0 -10
- data/test/dummy/tmp/generators/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
- data/test/dummy/tmp/generators/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be08ae7f01121ebe8c6b9b8fe04bcc2bdc83a2c8108452ffc986f1865278e85f
|
|
4
|
+
data.tar.gz: 272f45dc6f28fba16b6a523f47cbb9ecf3be9c05d4aa644ee0d0998fa5272f43
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc54c90eee4fdf43e6d9b72ca905fc58e4338f1310b289bbede651978bd4f407556392d3d4c61cfaeabf6d4fba179768e02ec9c00bc245b33ba629972676c676
|
|
7
|
+
data.tar.gz: 00e139ae99fe395580ef8f846cca46516792b68cda0e0201e551e90ca9c70679fcf9519d3682ea82095588e78a93bb2def3db2bebe670a0e96f933e7087fee4b
|
|
@@ -75,5 +75,22 @@ module DeviseTokenAuth
|
|
|
75
75
|
response = response.merge(data) if data
|
|
76
76
|
render json: response, status: status
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
def success_message(name, email)
|
|
80
|
+
if Devise.paranoid
|
|
81
|
+
I18n.t("devise_token_auth.#{name}.sended_paranoid")
|
|
82
|
+
else
|
|
83
|
+
I18n.t("devise_token_auth.#{name}.sended", email: email)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# When using a cookie to transport the auth token we can set it immediately in flows such as
|
|
88
|
+
# reset password and OmniAuth success, rather than making the client scrape the token from
|
|
89
|
+
# query params (to then send in the initial validate_token request).
|
|
90
|
+
# TODO: We should be able to stop exposing the token in query params when this method is used
|
|
91
|
+
def set_token_in_cookie(resource, token)
|
|
92
|
+
auth_header = resource.build_auth_header(token.token, token.client)
|
|
93
|
+
cookies[DeviseTokenAuth.cookie_name] = DeviseTokenAuth.cookie_attributes.merge(value: auth_header.to_json)
|
|
94
|
+
end
|
|
78
95
|
end
|
|
79
96
|
end
|
|
@@ -20,7 +20,7 @@ module DeviseTokenAuth::Concerns::ResourceFinder
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def find_resource(field, value)
|
|
23
|
-
@resource = if
|
|
23
|
+
@resource = if database_adapter&.include?('mysql')
|
|
24
24
|
# fix for mysql default case insensitivity
|
|
25
25
|
resource_class.where("BINARY #{field} = ? AND provider= ?", value, provider).first
|
|
26
26
|
else
|
|
@@ -28,6 +28,19 @@ module DeviseTokenAuth::Concerns::ResourceFinder
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def database_adapter
|
|
32
|
+
@database_adapter ||= begin
|
|
33
|
+
rails_version = [Rails::VERSION::MAJOR, Rails::VERSION::MINOR].join(".")
|
|
34
|
+
|
|
35
|
+
adapter =
|
|
36
|
+
if rails_version >= "6.1"
|
|
37
|
+
resource_class.try(:connection_db_config)&.try(:adapter)
|
|
38
|
+
else
|
|
39
|
+
resource_class.try(:connection_config)&.try(:[], :adapter)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
31
44
|
def resource_class(m = nil)
|
|
32
45
|
mapping = if m
|
|
33
46
|
Devise.mappings[m]
|
|
@@ -17,7 +17,7 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
17
17
|
@used_auth_by_token = true
|
|
18
18
|
|
|
19
19
|
# initialize instance variables
|
|
20
|
-
@token
|
|
20
|
+
@token ||= DeviseTokenAuth::TokenFactory.new
|
|
21
21
|
@resource ||= nil
|
|
22
22
|
@is_batch_request ||= nil
|
|
23
23
|
end
|
|
@@ -32,21 +32,36 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
32
32
|
|
|
33
33
|
# gets the headers names, which was set in the initialize file
|
|
34
34
|
uid_name = DeviseTokenAuth.headers_names[:'uid']
|
|
35
|
+
other_uid_name = DeviseTokenAuth.other_uid && DeviseTokenAuth.headers_names[DeviseTokenAuth.other_uid.to_sym]
|
|
35
36
|
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
|
|
36
37
|
client_name = DeviseTokenAuth.headers_names[:'client']
|
|
38
|
+
authorization_name = DeviseTokenAuth.headers_names[:"authorization"]
|
|
39
|
+
|
|
40
|
+
# Read Authorization token and decode it if present
|
|
41
|
+
decoded_authorization_token = decode_bearer_token(request.headers[authorization_name])
|
|
42
|
+
|
|
43
|
+
# gets values from cookie if configured and present
|
|
44
|
+
parsed_auth_cookie = {}
|
|
45
|
+
if DeviseTokenAuth.cookie_enabled
|
|
46
|
+
auth_cookie = request.cookies[DeviseTokenAuth.cookie_name]
|
|
47
|
+
if auth_cookie.present?
|
|
48
|
+
parsed_auth_cookie = JSON.parse(auth_cookie)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
37
51
|
|
|
38
52
|
# parse header for values necessary for authentication
|
|
39
|
-
uid = request.headers[uid_name] || params[uid_name]
|
|
53
|
+
uid = request.headers[uid_name] || params[uid_name] || parsed_auth_cookie[uid_name] || decoded_authorization_token[uid_name]
|
|
54
|
+
other_uid = other_uid_name && request.headers[other_uid_name] || params[other_uid_name] || parsed_auth_cookie[other_uid_name]
|
|
40
55
|
@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]
|
|
56
|
+
@token.token ||= request.headers[access_token_name] || params[access_token_name] || parsed_auth_cookie[access_token_name] || decoded_authorization_token[access_token_name]
|
|
57
|
+
@token.client ||= request.headers[client_name] || params[client_name] || parsed_auth_cookie[client_name] || decoded_authorization_token[client_name]
|
|
43
58
|
|
|
44
59
|
# client isn't required, set to 'default' if absent
|
|
45
60
|
@token.client ||= 'default'
|
|
46
61
|
|
|
47
62
|
# check for an existing user, authenticated via warden/devise, if enabled
|
|
48
63
|
if DeviseTokenAuth.enable_standard_devise_support
|
|
49
|
-
devise_warden_user = warden.user(
|
|
64
|
+
devise_warden_user = warden.user(mapping)
|
|
50
65
|
if devise_warden_user && devise_warden_user.tokens[@token.client].nil?
|
|
51
66
|
@used_auth_by_token = false
|
|
52
67
|
@resource = devise_warden_user
|
|
@@ -66,7 +81,7 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
# mitigate timing attacks by finding by uid instead of auth token
|
|
69
|
-
user = uid && rc.dta_find_by(uid: uid)
|
|
84
|
+
user = (uid && rc.dta_find_by(uid: uid)) || (other_uid && rc.dta_find_by("#{DeviseTokenAuth.other_uid}": other_uid))
|
|
70
85
|
scope = rc.to_s.underscore.to_sym
|
|
71
86
|
|
|
72
87
|
if user && user.valid_token?(@token.token, @token.client)
|
|
@@ -101,9 +116,13 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
101
116
|
# update the response header
|
|
102
117
|
response.headers.merge!(auth_header)
|
|
103
118
|
|
|
119
|
+
# set a server cookie if configured
|
|
120
|
+
if DeviseTokenAuth.cookie_enabled
|
|
121
|
+
set_cookie(auth_header)
|
|
122
|
+
end
|
|
104
123
|
else
|
|
105
124
|
unless @resource.reload.valid?
|
|
106
|
-
@resource =
|
|
125
|
+
@resource = @resource.class.find(@resource.to_param) # errors remain after reload
|
|
107
126
|
# if we left the model in a bad state, something is wrong in our app
|
|
108
127
|
unless @resource.valid?
|
|
109
128
|
raise DeviseTokenAuth::Errors::InvalidModel, "Cannot set auth token in invalid model. Errors: #{@resource.errors.full_messages}"
|
|
@@ -115,6 +134,13 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
115
134
|
|
|
116
135
|
private
|
|
117
136
|
|
|
137
|
+
def decode_bearer_token(bearer_token)
|
|
138
|
+
return {} if bearer_token.blank?
|
|
139
|
+
|
|
140
|
+
encoded_token = bearer_token.split.last # Removes the 'Bearer' from the string
|
|
141
|
+
JSON.parse(Base64.strict_decode64(encoded_token)) rescue {}
|
|
142
|
+
end
|
|
143
|
+
|
|
118
144
|
def refresh_headers
|
|
119
145
|
# Lock the user record during any auth_header updates to ensure
|
|
120
146
|
# we don't have write contention from multiple threads
|
|
@@ -123,11 +149,22 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
|
123
149
|
# cleared by sign out in the meantime
|
|
124
150
|
return if @used_auth_by_token && @resource.tokens[@token.client].nil?
|
|
125
151
|
|
|
152
|
+
_auth_header_from_batch_request = auth_header_from_batch_request
|
|
153
|
+
|
|
126
154
|
# update the response header
|
|
127
|
-
response.headers.merge!(
|
|
155
|
+
response.headers.merge!(_auth_header_from_batch_request)
|
|
156
|
+
|
|
157
|
+
# set a server cookie if configured
|
|
158
|
+
if DeviseTokenAuth.cookie_enabled
|
|
159
|
+
set_cookie(_auth_header_from_batch_request)
|
|
160
|
+
end
|
|
128
161
|
end # end lock
|
|
129
162
|
end
|
|
130
163
|
|
|
164
|
+
def set_cookie(auth_header)
|
|
165
|
+
cookies[DeviseTokenAuth.cookie_name] = DeviseTokenAuth.cookie_attributes.merge(value: auth_header.to_json)
|
|
166
|
+
end
|
|
167
|
+
|
|
131
168
|
def is_batch_request?(user, client)
|
|
132
169
|
!params[:unbatch] &&
|
|
133
170
|
user.tokens[client] &&
|
|
@@ -55,13 +55,17 @@ module DeviseTokenAuth
|
|
|
55
55
|
|
|
56
56
|
def render_create_success
|
|
57
57
|
render json: {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
success: true,
|
|
59
|
+
message: success_message('confirmations', @email)
|
|
60
|
+
}
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def render_not_found_error
|
|
64
|
-
|
|
64
|
+
if Devise.paranoid
|
|
65
|
+
render_create_success
|
|
66
|
+
else
|
|
67
|
+
render_error(404, I18n.t('devise_token_auth.confirmations.user_not_found', email: @email))
|
|
68
|
+
end
|
|
65
69
|
end
|
|
66
70
|
|
|
67
71
|
private
|
|
@@ -23,7 +23,7 @@ module DeviseTokenAuth
|
|
|
23
23
|
session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
|
|
24
24
|
session['dta.omniauth.params'] = request.env['omniauth.params']
|
|
25
25
|
|
|
26
|
-
redirect_to redirect_route
|
|
26
|
+
redirect_to redirect_route, status: 307
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def get_redirect_route(devise_mapping)
|
|
@@ -45,7 +45,7 @@ module DeviseTokenAuth
|
|
|
45
45
|
# find the mapping in `omniauth.params`.
|
|
46
46
|
#
|
|
47
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
|
|
48
|
+
# case, there will have been no initial request in which to save
|
|
49
49
|
# the devise mapping. If you are in a situation like that, and
|
|
50
50
|
# your app allows for you to determine somehow what the devise
|
|
51
51
|
# mapping should be (because, for example, it is always the same),
|
|
@@ -70,6 +70,10 @@ module DeviseTokenAuth
|
|
|
70
70
|
|
|
71
71
|
yield @resource if block_given?
|
|
72
72
|
|
|
73
|
+
if DeviseTokenAuth.cookie_enabled
|
|
74
|
+
set_token_in_cookie(@resource, @token)
|
|
75
|
+
end
|
|
76
|
+
|
|
73
77
|
render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
|
|
74
78
|
end
|
|
75
79
|
|
|
@@ -78,10 +82,10 @@ module DeviseTokenAuth
|
|
|
78
82
|
render_data_or_redirect('authFailure', error: @error)
|
|
79
83
|
end
|
|
80
84
|
|
|
81
|
-
def validate_auth_origin_url_param
|
|
85
|
+
def validate_auth_origin_url_param
|
|
82
86
|
return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
|
|
83
87
|
end
|
|
84
|
-
|
|
88
|
+
|
|
85
89
|
|
|
86
90
|
protected
|
|
87
91
|
|
|
@@ -51,6 +51,10 @@ module DeviseTokenAuth
|
|
|
51
51
|
if require_client_password_reset_token?
|
|
52
52
|
redirect_to DeviseTokenAuth::Url.generate(@redirect_url, reset_password_token: resource_params[:reset_password_token])
|
|
53
53
|
else
|
|
54
|
+
if DeviseTokenAuth.cookie_enabled
|
|
55
|
+
set_token_in_cookie(@resource, token)
|
|
56
|
+
end
|
|
57
|
+
|
|
54
58
|
redirect_header_options = { reset_password: true }
|
|
55
59
|
redirect_headers = build_redirect_headers(token.token,
|
|
56
60
|
token.client,
|
|
@@ -128,7 +132,7 @@ module DeviseTokenAuth
|
|
|
128
132
|
def render_create_success
|
|
129
133
|
render json: {
|
|
130
134
|
success: true,
|
|
131
|
-
message:
|
|
135
|
+
message: success_message('passwords', @email)
|
|
132
136
|
}
|
|
133
137
|
end
|
|
134
138
|
|
|
@@ -181,7 +185,11 @@ module DeviseTokenAuth
|
|
|
181
185
|
end
|
|
182
186
|
|
|
183
187
|
def render_not_found_error
|
|
184
|
-
|
|
188
|
+
if Devise.paranoid
|
|
189
|
+
render_create_success
|
|
190
|
+
else
|
|
191
|
+
render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
|
|
192
|
+
end
|
|
185
193
|
end
|
|
186
194
|
|
|
187
195
|
def validate_redirect_url_param
|
|
@@ -26,8 +26,8 @@ module DeviseTokenAuth
|
|
|
26
26
|
if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
|
|
27
27
|
return render_create_error_bad_credentials
|
|
28
28
|
end
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
|
|
30
|
+
create_and_assign_token
|
|
31
31
|
|
|
32
32
|
sign_in(:user, @resource, store: false, bypass: false)
|
|
33
33
|
|
|
@@ -48,13 +48,19 @@ module DeviseTokenAuth
|
|
|
48
48
|
def destroy
|
|
49
49
|
# remove auth instance variables so that after_action does not run
|
|
50
50
|
user = remove_instance_variable(:@resource) if @resource
|
|
51
|
-
client = @token.client
|
|
51
|
+
client = @token.client
|
|
52
52
|
@token.clear!
|
|
53
53
|
|
|
54
54
|
if user && client && user.tokens[client]
|
|
55
55
|
user.tokens.delete(client)
|
|
56
56
|
user.save!
|
|
57
57
|
|
|
58
|
+
if DeviseTokenAuth.cookie_enabled
|
|
59
|
+
# If a cookie is set with a domain specified then it must be deleted with that domain specified
|
|
60
|
+
# See https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
|
|
61
|
+
cookies.delete(DeviseTokenAuth.cookie_name, domain: DeviseTokenAuth.cookie_attributes[:domain])
|
|
62
|
+
end
|
|
63
|
+
|
|
58
64
|
yield user if block_given?
|
|
59
65
|
|
|
60
66
|
render_destroy_success
|
|
@@ -127,5 +133,17 @@ module DeviseTokenAuth
|
|
|
127
133
|
def resource_params
|
|
128
134
|
params.permit(*params_for_resource(:sign_in))
|
|
129
135
|
end
|
|
136
|
+
|
|
137
|
+
def create_and_assign_token
|
|
138
|
+
if @resource.respond_to?(:with_lock)
|
|
139
|
+
@resource.with_lock do
|
|
140
|
+
@token = @resource.create_token
|
|
141
|
+
@resource.save!
|
|
142
|
+
end
|
|
143
|
+
else
|
|
144
|
+
@token = @resource.create_token
|
|
145
|
+
@resource.save!
|
|
146
|
+
end
|
|
147
|
+
end
|
|
130
148
|
end
|
|
131
149
|
end
|
|
@@ -63,7 +63,7 @@ module DeviseTokenAuth
|
|
|
63
63
|
def render_create_success
|
|
64
64
|
render json: {
|
|
65
65
|
success: true,
|
|
66
|
-
message:
|
|
66
|
+
message: success_message('unlocks', @email)
|
|
67
67
|
}
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -79,7 +79,11 @@ module DeviseTokenAuth
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def render_not_found_error
|
|
82
|
-
|
|
82
|
+
if Devise.paranoid
|
|
83
|
+
render_create_success
|
|
84
|
+
else
|
|
85
|
+
render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
|
|
86
|
+
end
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
def resource_params
|
|
@@ -18,7 +18,8 @@ module DeviseTokenAuth::Concerns::ConfirmableSupport
|
|
|
18
18
|
protected
|
|
19
19
|
|
|
20
20
|
def email_value_in_database
|
|
21
|
-
|
|
21
|
+
rails51 = Rails.gem_version >= Gem::Version.new("5.1.x")
|
|
22
|
+
if rails51 && respond_to?(:email_in_database)
|
|
22
23
|
email_in_database
|
|
23
24
|
else
|
|
24
25
|
email_was
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
module DeviseTokenAuth::Concerns::TokensSerialization
|
|
2
|
+
extend self
|
|
2
3
|
# Serialization hash to json
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
def dump(object)
|
|
5
|
+
JSON.generate(object && object.transform_values do |token|
|
|
6
|
+
serialize_updated_at(token).compact
|
|
7
|
+
end.compact)
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
# Deserialization json to hash
|
|
9
|
-
def
|
|
11
|
+
def load(json)
|
|
10
12
|
case json
|
|
11
13
|
when String
|
|
12
14
|
JSON.parse(json)
|
|
@@ -16,4 +18,14 @@ module DeviseTokenAuth::Concerns::TokensSerialization
|
|
|
16
18
|
json
|
|
17
19
|
end
|
|
18
20
|
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def serialize_updated_at(token)
|
|
25
|
+
updated_at_key = ['updated_at', :updated_at].find(&token.method(:[]))
|
|
26
|
+
|
|
27
|
+
return token unless token[updated_at_key].respond_to?(:iso8601)
|
|
28
|
+
|
|
29
|
+
token.merge updated_at_key => token[updated_at_key].iso8601
|
|
30
|
+
end
|
|
19
31
|
end
|
|
@@ -120,6 +120,7 @@ module DeviseTokenAuth::Concerns::User
|
|
|
120
120
|
# ghetto HashWithIndifferentAccess
|
|
121
121
|
expiry = tokens[client]['expiry'] || tokens[client][:expiry]
|
|
122
122
|
token_hash = tokens[client]['token'] || tokens[client][:token]
|
|
123
|
+
previous_token_hash = tokens[client]['previous_token'] || tokens[client][:previous_token]
|
|
123
124
|
|
|
124
125
|
return true if (
|
|
125
126
|
# ensure that expiry and token are set
|
|
@@ -129,11 +130,24 @@ module DeviseTokenAuth::Concerns::User
|
|
|
129
130
|
DateTime.strptime(expiry.to_s, '%s') > Time.zone.now &&
|
|
130
131
|
|
|
131
132
|
# ensure that the token is valid
|
|
132
|
-
|
|
133
|
+
(
|
|
134
|
+
# check if the latest token matches
|
|
135
|
+
does_token_match?(token_hash, token) ||
|
|
136
|
+
|
|
137
|
+
# check if the previous token matches
|
|
138
|
+
does_token_match?(previous_token_hash, token)
|
|
139
|
+
)
|
|
133
140
|
)
|
|
134
141
|
end
|
|
135
142
|
|
|
136
|
-
#
|
|
143
|
+
# check if the hash of received token matches the stored token
|
|
144
|
+
def does_token_match?(token_hash, token)
|
|
145
|
+
return false if token_hash.nil?
|
|
146
|
+
|
|
147
|
+
DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# allow batch requests to use the last token
|
|
137
151
|
def token_can_be_reused?(token, client)
|
|
138
152
|
# ghetto HashWithIndifferentAccess
|
|
139
153
|
updated_at = tokens[client]['updated_at'] || tokens[client][:updated_at]
|
|
@@ -143,7 +157,7 @@ module DeviseTokenAuth::Concerns::User
|
|
|
143
157
|
# ensure that the last token and its creation time exist
|
|
144
158
|
updated_at && last_token_hash &&
|
|
145
159
|
|
|
146
|
-
# ensure that
|
|
160
|
+
# ensure that last token falls within the batch buffer throttle time of the last request
|
|
147
161
|
updated_at.to_time > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
|
|
148
162
|
|
|
149
163
|
# ensure that the token is valid
|
|
@@ -157,8 +171,9 @@ module DeviseTokenAuth::Concerns::User
|
|
|
157
171
|
|
|
158
172
|
token = create_token(
|
|
159
173
|
client: client,
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
previous_token: tokens.fetch(client, {})['token'],
|
|
175
|
+
last_token: tokens.fetch(client, {})['previous_token'],
|
|
176
|
+
updated_at: now
|
|
162
177
|
)
|
|
163
178
|
|
|
164
179
|
update_auth_header(token.token, token.client)
|
|
@@ -168,14 +183,20 @@ module DeviseTokenAuth::Concerns::User
|
|
|
168
183
|
# client may use expiry to prevent validation request if expired
|
|
169
184
|
# must be cast as string or headers will break
|
|
170
185
|
expiry = tokens[client]['expiry'] || tokens[client][:expiry]
|
|
171
|
-
|
|
172
|
-
{
|
|
186
|
+
headers = {
|
|
173
187
|
DeviseTokenAuth.headers_names[:"access-token"] => token,
|
|
174
188
|
DeviseTokenAuth.headers_names[:"token-type"] => 'Bearer',
|
|
175
189
|
DeviseTokenAuth.headers_names[:"client"] => client,
|
|
176
190
|
DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
|
|
177
191
|
DeviseTokenAuth.headers_names[:"uid"] => uid
|
|
178
192
|
}
|
|
193
|
+
headers.merge!(build_bearer_token(headers))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def build_bearer_token(auth)
|
|
197
|
+
encoded_token = Base64.strict_encode64(auth.to_json)
|
|
198
|
+
bearer_token = "Bearer #{encoded_token}"
|
|
199
|
+
{DeviseTokenAuth.headers_names[:"authorization"] => bearer_token}
|
|
179
200
|
end
|
|
180
201
|
|
|
181
202
|
def update_auth_header(token, client = 'default')
|
|
@@ -194,7 +215,7 @@ module DeviseTokenAuth::Concerns::User
|
|
|
194
215
|
end
|
|
195
216
|
|
|
196
217
|
def extend_batch_buffer(token, client)
|
|
197
|
-
tokens[client]['updated_at'] = Time.zone.now
|
|
218
|
+
tokens[client]['updated_at'] = Time.zone.now
|
|
198
219
|
update_auth_header(token, client)
|
|
199
220
|
end
|
|
200
221
|
|
|
@@ -218,13 +239,8 @@ module DeviseTokenAuth::Concerns::User
|
|
|
218
239
|
end
|
|
219
240
|
|
|
220
241
|
def should_remove_tokens_after_password_reset?
|
|
221
|
-
|
|
222
|
-
encrypted_password_changed? &&
|
|
223
|
-
DeviseTokenAuth.remove_tokens_after_password_reset
|
|
224
|
-
else
|
|
225
|
-
saved_change_to_attribute?(:encrypted_password) &&
|
|
226
|
-
DeviseTokenAuth.remove_tokens_after_password_reset
|
|
227
|
-
end
|
|
242
|
+
DeviseTokenAuth.remove_tokens_after_password_reset &&
|
|
243
|
+
(respond_to?(:encrypted_password_changed?) && encrypted_password_changed?)
|
|
228
244
|
end
|
|
229
245
|
|
|
230
246
|
def remove_tokens_after_password_reset
|
|
@@ -4,12 +4,12 @@ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
included do
|
|
7
|
-
validates :email, presence: true,if:
|
|
8
|
-
validates :email, :devise_token_auth_email => true, allow_nil: true, allow_blank: true, if:
|
|
9
|
-
validates_presence_of :uid,
|
|
7
|
+
validates :email, presence: true, if: lambda { uid_and_provider_defined? && email_provider? }
|
|
8
|
+
validates :email, :devise_token_auth_email => true, allow_nil: true, allow_blank: true, if: lambda { uid_and_provider_defined? && email_provider? }
|
|
9
|
+
validates_presence_of :uid, if: lambda { uid_and_provider_defined? && !email_provider? }
|
|
10
10
|
|
|
11
11
|
# only validate unique emails among email registration users
|
|
12
|
-
validates :email, uniqueness: { case_sensitive: false, scope: :provider }, on: :create, if:
|
|
12
|
+
validates :email, uniqueness: { case_sensitive: false, scope: :provider }, on: :create, if: lambda { uid_and_provider_defined? && email_provider? }
|
|
13
13
|
|
|
14
14
|
# keep uid in sync with email
|
|
15
15
|
before_save :sync_uid
|
|
@@ -18,11 +18,18 @@ module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
|
|
|
18
18
|
|
|
19
19
|
protected
|
|
20
20
|
|
|
21
|
+
def uid_and_provider_defined?
|
|
22
|
+
defined?(provider) && defined?(uid)
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def email_provider?
|
|
22
26
|
provider == 'email'
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def sync_uid
|
|
26
|
-
self.
|
|
30
|
+
unless self.new_record?
|
|
31
|
+
return if devise_modules.include?(:confirmable) && !@bypass_confirmation_postpone && postpone_email_change?
|
|
32
|
+
end
|
|
33
|
+
self.uid = email if uid_and_provider_defined? && email_provider?
|
|
27
34
|
end
|
|
28
35
|
end
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class DeviseTokenAuthEmailValidator < ActiveModel::EachValidator
|
|
4
|
+
EMAIL_REGEXP = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def validate?(email)
|
|
8
|
+
email =~ EMAIL_REGEXP
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
def validate_each(record, attribute, value)
|
|
5
|
-
unless value
|
|
6
|
-
record.errors
|
|
13
|
+
unless DeviseTokenAuthEmailValidator.validate?(value)
|
|
14
|
+
record.errors.add(attribute, email_invalid_message)
|
|
7
15
|
end
|
|
8
16
|
end
|
|
9
17
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
Cordova / PhoneGap)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
var data = JSON.parse(decodeURIComponent('<%=
|
|
18
|
+
var data = JSON.parse(decodeURIComponent('<%= ERB::Util.url_encode( @data.to_json ) %>'));
|
|
19
19
|
|
|
20
20
|
window.addEventListener("message", function(ev) {
|
|
21
21
|
if (ev.data === "requestCredentials") {
|
data/config/locales/en.yml
CHANGED
|
@@ -21,6 +21,7 @@ en:
|
|
|
21
21
|
missing_redirect_url: "Missing redirect URL."
|
|
22
22
|
not_allowed_redirect_url: "Redirect to '%{redirect_url}' not allowed."
|
|
23
23
|
sended: "An email has been sent to '%{email}' containing instructions for resetting your password."
|
|
24
|
+
sended_paranoid: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
|
|
24
25
|
user_not_found: "Unable to find user with email '%{email}'."
|
|
25
26
|
password_not_required: "This account does not require a password. Sign in using your '%{provider}' account instead."
|
|
26
27
|
missing_passwords: "You must fill out the fields labeled 'Password' and 'Password confirmation'."
|
|
@@ -28,9 +29,11 @@ en:
|
|
|
28
29
|
unlocks:
|
|
29
30
|
missing_email: "You must provide an email address."
|
|
30
31
|
sended: "An email has been sent to '%{email}' containing instructions for unlocking your account."
|
|
32
|
+
sended_paranoid: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
|
|
31
33
|
user_not_found: "Unable to find user with email '%{email}'."
|
|
32
34
|
confirmations:
|
|
33
35
|
sended: "An email has been sent to '%{email}' containing instructions for confirming your account."
|
|
36
|
+
sended_paranoid: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
|
|
34
37
|
user_not_found: "Unable to find user with email '%{email}'."
|
|
35
38
|
missing_email: "You must provide an email address."
|
|
36
39
|
|
data/config/locales/ja.yml
CHANGED
|
@@ -21,10 +21,22 @@ ja:
|
|
|
21
21
|
missing_redirect_url: "リダイレクト URL が与えられていません。"
|
|
22
22
|
not_allowed_redirect_url: "'%{redirect_url}' へのリダイレクトは許可されていません。"
|
|
23
23
|
sended: "'%{email}' にパスワードリセットの案内が送信されました。"
|
|
24
|
+
sended_paranoid: "すでにメールアドレスがデータベースに登録されている場合、 数分後にパスワード再発行用のリンクを記載したメールをお送りします。"
|
|
24
25
|
user_not_found: "メールアドレス '%{email}' のユーザーが見つかりません。"
|
|
25
26
|
password_not_required: "このアカウントはパスワードを要求していません。'%{provider}' を利用してログインしてください。"
|
|
26
27
|
missing_passwords: "'Password', 'Password confirmation' パラメータが与えられていません。"
|
|
27
28
|
successfully_updated: "パスワードの更新に成功しました。"
|
|
29
|
+
unlocks:
|
|
30
|
+
missing_email: "メールアドレスが与えられていません。"
|
|
31
|
+
sended: "%{email}' にアカウントのロックを解除する方法を記載したメールが送信されました。"
|
|
32
|
+
sended_paranoid: "アカウントが存在する場合、数分後にロックを解除する方法を記載したメールをお送りします。"
|
|
33
|
+
user_not_found: "メールアドレス '%{email}' を持つユーザーが見つかりません。"
|
|
34
|
+
confirmations:
|
|
35
|
+
sended: "'%{email}' にアカウントの確認方法を記載したメールが送信されました。"
|
|
36
|
+
sended_paranoid: "すでにメールアドレスがデータベースに登録されている場合、数分後にメールアドレスの確認方法を記載したメールをお送りします。"
|
|
37
|
+
user_not_found: "メールアドレス '%{email}' を持つユーザーが見つかりません。"
|
|
38
|
+
missing_email: "メールアドレスが与えられていません。"
|
|
39
|
+
|
|
28
40
|
errors:
|
|
29
41
|
messages:
|
|
30
42
|
validate_sign_up_params: "リクエストボディに適切なアカウント新規登録データを送信してください。"
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
# don't serialize tokens
|
|
2
|
-
Devise::Models::Authenticatable::
|
|
2
|
+
if defined? Devise::Models::Authenticatable::UNSAFE_ATTRIBUTES_FOR_SERIALIZATION
|
|
3
|
+
Devise::Models::Authenticatable::UNSAFE_ATTRIBUTES_FOR_SERIALIZATION << :tokens
|
|
4
|
+
else
|
|
5
|
+
Devise::Models::Authenticatable::BLACKLIST_FOR_SERIALIZATION << :tokens
|
|
6
|
+
end
|
|
@@ -34,12 +34,6 @@ module DeviseTokenAuth
|
|
|
34
34
|
class_eval <<-METHODS, __FILE__, __LINE__ + 1
|
|
35
35
|
def authenticate_#{group_name}!(favourite=nil, opts={})
|
|
36
36
|
unless #{group_name}_signed_in?
|
|
37
|
-
mappings = #{mappings}
|
|
38
|
-
mappings.unshift mappings.delete(favourite.to_sym) if favourite
|
|
39
|
-
mappings.each do |mapping|
|
|
40
|
-
set_user_by_token(mapping)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
37
|
unless current_#{group_name}
|
|
44
38
|
render_authenticate_error
|
|
45
39
|
end
|
|
@@ -47,12 +41,14 @@ module DeviseTokenAuth
|
|
|
47
41
|
end
|
|
48
42
|
|
|
49
43
|
def #{group_name}_signed_in?
|
|
50
|
-
#{
|
|
51
|
-
set_user_by_token(mapping)
|
|
52
|
-
end
|
|
44
|
+
!!current_#{group_name}
|
|
53
45
|
end
|
|
54
46
|
|
|
55
47
|
def current_#{group_name}(favourite=nil)
|
|
48
|
+
@current_#{group_name} ||= set_group_user_by_token(favourite)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def set_group_user_by_token(favourite)
|
|
56
52
|
mappings = #{mappings}
|
|
57
53
|
mappings.unshift mappings.delete(favourite.to_sym) if favourite
|
|
58
54
|
mappings.each do |mapping|
|