devise_token_auth 1.0.0 → 1.2.1

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