devise_token_auth 1.1.4 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/devise_token_auth/application_controller.rb +17 -0
  3. data/app/controllers/devise_token_auth/concerns/resource_finder.rb +14 -1
  4. data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +45 -8
  5. data/app/controllers/devise_token_auth/confirmations_controller.rb +8 -4
  6. data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +8 -4
  7. data/app/controllers/devise_token_auth/passwords_controller.rb +10 -2
  8. data/app/controllers/devise_token_auth/sessions_controller.rb +21 -3
  9. data/app/controllers/devise_token_auth/unlocks_controller.rb +6 -2
  10. data/app/models/devise_token_auth/concerns/active_record_support.rb +0 -2
  11. data/app/models/devise_token_auth/concerns/confirmable_support.rb +2 -1
  12. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +16 -4
  13. data/app/models/devise_token_auth/concerns/user.rb +31 -15
  14. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +12 -5
  15. data/app/validators/devise_token_auth_email_validator.rb +10 -2
  16. data/app/views/devise_token_auth/omniauth_external_window.html.erb +1 -1
  17. data/config/locales/en.yml +3 -0
  18. data/config/locales/ja.yml +12 -0
  19. data/lib/devise_token_auth/blacklist.rb +5 -1
  20. data/lib/devise_token_auth/controllers/helpers.rb +5 -9
  21. data/lib/devise_token_auth/engine.rb +11 -2
  22. data/lib/devise_token_auth/rails/routes.rb +17 -12
  23. data/lib/devise_token_auth/version.rb +1 -1
  24. data/lib/generators/devise_token_auth/install_generator.rb +1 -1
  25. data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +3 -0
  26. data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +1 -1
  27. data/test/controllers/demo_mang_controller_test.rb +37 -8
  28. data/test/controllers/demo_user_controller_test.rb +37 -8
  29. data/test/controllers/devise_token_auth/confirmations_controller_test.rb +100 -19
  30. data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +2 -2
  31. data/test/controllers/devise_token_auth/passwords_controller_test.rb +73 -21
  32. data/test/controllers/devise_token_auth/registrations_controller_test.rb +28 -15
  33. data/test/controllers/devise_token_auth/sessions_controller_test.rb +39 -10
  34. data/test/controllers/devise_token_auth/token_validations_controller_test.rb +41 -1
  35. data/test/controllers/devise_token_auth/unlocks_controller_test.rb +44 -5
  36. data/test/controllers/overrides/confirmations_controller_test.rb +1 -1
  37. data/test/dummy/app/views/layouts/application.html.erb +0 -2
  38. data/test/dummy/config/application.rb +0 -1
  39. data/test/dummy/config/environments/development.rb +0 -10
  40. data/test/dummy/config/environments/production.rb +0 -16
  41. data/test/dummy/db/schema.rb +5 -5
  42. data/test/dummy/tmp/generators/app/models/user.rb +11 -0
  43. data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
  44. data/test/dummy/tmp/generators/db/migrate/20220822003050_devise_token_auth_create_users.rb +49 -0
  45. data/test/lib/devise_token_auth/blacklist_test.rb +11 -3
  46. data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
  47. data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
  48. data/test/lib/generators/devise_token_auth/install_generator_test.rb +1 -1
  49. data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +1 -1
  50. data/test/models/concerns/tokens_serialization_test.rb +39 -5
  51. data/test/models/user_test.rb +22 -0
  52. data/test/test_helper.rb +35 -4
  53. metadata +16 -26
  54. data/test/dummy/config/initializers/assets.rb +0 -10
  55. data/test/dummy/tmp/generators/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
  56. 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: 395c104491ef2762e5c41f0b35af5f2421f8d24c99cc10145231d1cb2cab2d70
4
- data.tar.gz: c637be9bc9c731f1b6218002925c0e558dbc62f2d6fb999fdd187d31d60e20c4
3
+ metadata.gz: be08ae7f01121ebe8c6b9b8fe04bcc2bdc83a2c8108452ffc986f1865278e85f
4
+ data.tar.gz: 272f45dc6f28fba16b6a523f47cbb9ecf3be9c05d4aa644ee0d0998fa5272f43
5
5
  SHA512:
6
- metadata.gz: a1a184d38110e9157c941f1b5e2b8a0cdd7901702f12c7316a4ffba2b5af239455bddc9c288d8fbbd2c909aadfdfe388283c16abcce1814abf595cfe853e3c51
7
- data.tar.gz: 7ac1939d622a50f46e9ce3943826b85e67e9457178bba79326c5656f4c8fbacc5205b44828aa4935be4c2c4dc713f68ab1d44b8d7485ced86fa90416769e1431
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 resource_class.try(:connection_config).try(:[], :adapter).try(:include?, 'mysql')
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 = DeviseTokenAuth::TokenFactory.new
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(rc.to_s.underscore.to_sym)
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 = resource_class.find(@resource.to_param) # errors remain after reload
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!(auth_header_from_batch_request)
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
- success: true,
59
- message: I18n.t('devise_token_auth.confirmations.sended', email: @email)
60
- }
58
+ success: true,
59
+ message: success_message('confirmations', @email)
60
+ }
61
61
  end
62
62
 
63
63
  def render_not_found_error
64
- render_error(404, I18n.t('devise_token_auth.confirmations.user_not_found', email: @email))
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: I18n.t('devise_token_auth.passwords.sended', email: @email)
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
- render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
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
- @token = @resource.create_token
30
- @resource.save
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 if @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: I18n.t('devise_token_auth.unlocks.sended', email: @email)
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
- render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
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
@@ -1,5 +1,3 @@
1
- require_relative 'tokens_serialization'
2
-
3
1
  module DeviseTokenAuth::Concerns::ActiveRecordSupport
4
2
  extend ActiveSupport::Concern
5
3
 
@@ -18,7 +18,8 @@ module DeviseTokenAuth::Concerns::ConfirmableSupport
18
18
  protected
19
19
 
20
20
  def email_value_in_database
21
- if Devise.rails51? && respond_to?(:email_in_database)
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 self.dump(object)
4
- object.each_value(&:compact!) unless object.nil?
5
- JSON.generate(object)
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 self.load(json)
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
- DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
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
- # allow batch requests to use the previous token
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 previous token falls within the batch buffer throttle time of the last request
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
- last_token: tokens.fetch(client, {})['token'],
161
- updated_at: now.to_s(:rfc822)
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.to_s(:rfc822)
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
- if Rails::VERSION::MAJOR <= 5 ||defined?('Mongoid')
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: :email_provider?
8
- validates :email, :devise_token_auth_email => true, allow_nil: true, allow_blank: true, if: :email_provider?
9
- validates_presence_of :uid, unless: :email_provider?
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: :email_provider?
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.uid = email if email_provider?
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 =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
6
- record.errors[attribute] << email_invalid_message
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('<%= URI::escape( @data.to_json ) %>'));
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") {
@@ -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
 
@@ -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::BLACKLIST_FOR_SERIALIZATION << :tokens
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
- #{mappings}.any? do |mapping|
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|