devise_token_auth 1.0.0 → 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.
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
@@ -14,6 +14,8 @@ ro:
14
14
  account_with_uid_destroyed: "Contul cu UID '%{uid}' a fost șters."
15
15
  account_to_destroy_not_found: "Nu se poate localiza contul pentru ștergere."
16
16
  user_not_found: "Utilizatorul nu a fost găsit."
17
+ omniauth:
18
+ not_allowed_redirect_url: "Redirecționarea către '%{redirect_url}' nu este permisă."
17
19
  passwords:
18
20
  missing_email: "Trebuie să introduci o adresă de e-mail."
19
21
  missing_redirect_url: "URL-ul pentru redirecționare lipsește."
@@ -14,6 +14,8 @@ ru:
14
14
  account_with_uid_destroyed: "Учетная запись с uid '%{uid}' удалена."
15
15
  account_to_destroy_not_found: "Не удается найти учетную запись для удаления."
16
16
  user_not_found: "Пользователь не найден."
17
+ omniauth:
18
+ not_allowed_redirect_url: "Переадресация на '%{redirect_url}' не разрешена."
17
19
  passwords:
18
20
  missing_email: "Вы должны указать адрес электронной почты."
19
21
  missing_redirect_url: "Отсутствует адрес переадресации."
@@ -14,6 +14,8 @@ sq:
14
14
  account_with_uid_destroyed: "Llogaria me UID-në '%{uid}' është fshirë."
15
15
  account_to_destroy_not_found: "Nuk u gjet llogaria për fshirje."
16
16
  user_not_found: "Përdoruesi nuk u gjet."
17
+ omniauth:
18
+ not_allowed_redirect_url: "Nuk lejohet shkuarja tek URL-ja '%{redirect_url}'."
17
19
  passwords:
18
20
  missing_email: "Ju duhet të jepni një email adresë."
19
21
  missing_redirect_url: "Mungon URL-ja për ridërgim."
@@ -14,6 +14,8 @@ sv:
14
14
  account_with_uid_destroyed: "Kontot med UID '%{uid}' har tagits bort."
15
15
  account_to_destroy_not_found: "Kunde inte hitta kontot för borttagning."
16
16
  user_not_found: "Användaren hittades ej."
17
+ omniauth:
18
+ not_allowed_redirect_url: "Omdirigering till '%{redirect_url}' ej tillåten."
17
19
  passwords:
18
20
  missing_email: "Du måste ange en emailadress."
19
21
  missing_redirect_url: "Saknar en omdirigerings-URL."
@@ -14,6 +14,8 @@ uk:
14
14
  account_with_uid_destroyed: "Акаунт з UID '%{uid}' було видалено."
15
15
  account_to_destroy_not_found: "Неможливо знайти акаунт для видалення."
16
16
  user_not_found: "Користувача не знайдено"
17
+ omniauth:
18
+ not_allowed_redirect_url: "Перенаправлення до '%{redirect_url}' не дозволено."
17
19
  passwords:
18
20
  missing_email: "Ви маєте ввести email адресу."
19
21
  missing_redirect_url: "Немає URL для перенаправлення."
@@ -14,6 +14,8 @@ vi:
14
14
  account_with_uid_destroyed: "Tài khoản với UID '%{uid}' vừa bị phá hủy."
15
15
  account_to_destroy_not_found: "Không thể xác định tài khoản cho việc phá hủy."
16
16
  user_not_found: "Người dùng không tìm thấy."
17
+ omniauth:
18
+ not_allowed_redirect_url: "Chuyển hướng tới '%{redirect_url}' không được phép."
17
19
  passwords:
18
20
  missing_email: "Bạn cần cung cấp địa chỉ email."
19
21
  missing_redirect_url: "Thiếu đường đẫn URL."
@@ -14,6 +14,8 @@ zh-CN:
14
14
  account_with_uid_destroyed: "账号 '%{uid}' 已被移除。"
15
15
  account_to_destroy_not_found: "无法找到目标帐号。"
16
16
  user_not_found: "找不到帐号。"
17
+ omniauth:
18
+ not_allowed_redirect_url: "不支持转向到 '%{redirect_url}'"
17
19
  passwords:
18
20
  missing_email: "必需提供邮箱。"
19
21
  missing_redirect_url: "欠缺 redirect URL."
@@ -16,6 +16,8 @@ zh-TW:
16
16
  account_with_uid_destroyed: "帳號 '%{uid}' 已被移除。"
17
17
  account_to_destroy_not_found: "無法找到目標帳號。"
18
18
  user_not_found: "找不到帳號。"
19
+ omniauth:
20
+ not_allowed_redirect_url: "不支援轉向到 '%{redirect_url}'"
19
21
  passwords:
20
22
  missing_email: "必需提供電郵。"
21
23
  missing_redirect_url: "欠缺 redirect URL."
@@ -16,6 +16,8 @@ zh-TW:
16
16
  account_with_uid_destroyed: "帳號 '%{uid}' 已被移除。"
17
17
  account_to_destroy_not_found: "無法找到目標帳號。"
18
18
  user_not_found: "找不到帳號。"
19
+ omniauth:
20
+ not_allowed_redirect_url: "不支援轉向到 '%{redirect_url}'"
19
21
  passwords:
20
22
  missing_email: "必需提供電郵。"
21
23
  missing_redirect_url: "欠缺 redirect URL."
@@ -0,0 +1,6 @@
1
+ # don't serialize 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|
@@ -14,6 +14,7 @@ module DeviseTokenAuth
14
14
  mattr_accessor :change_headers_on_each_request,
15
15
  :max_number_of_devices,
16
16
  :token_lifespan,
17
+ :token_cost,
17
18
  :batch_request_buffer_throttle,
18
19
  :omniauth_prefix,
19
20
  :default_confirm_success_url,
@@ -24,11 +25,18 @@ module DeviseTokenAuth
24
25
  :remove_tokens_after_password_reset,
25
26
  :default_callbacks,
26
27
  :headers_names,
27
- :bypass_sign_in
28
+ :cookie_enabled,
29
+ :cookie_name,
30
+ :cookie_attributes,
31
+ :bypass_sign_in,
32
+ :send_confirmation_email,
33
+ :require_client_password_reset_token,
34
+ :other_uid
28
35
 
29
36
  self.change_headers_on_each_request = true
30
37
  self.max_number_of_devices = 10
31
38
  self.token_lifespan = 2.weeks
39
+ self.token_cost = 10
32
40
  self.batch_request_buffer_throttle = 5.seconds
33
41
  self.omniauth_prefix = '/omniauth'
34
42
  self.default_confirm_success_url = nil
@@ -38,12 +46,19 @@ module DeviseTokenAuth
38
46
  self.enable_standard_devise_support = false
39
47
  self.remove_tokens_after_password_reset = false
40
48
  self.default_callbacks = true
41
- self.headers_names = { 'access-token': 'access-token',
49
+ self.headers_names = { 'authorization': 'Authorization',
50
+ 'access-token': 'access-token',
42
51
  'client': 'client',
43
52
  'expiry': 'expiry',
44
53
  'uid': 'uid',
45
54
  'token-type': 'token-type' }
55
+ self.cookie_enabled = false
56
+ self.cookie_name = 'auth_cookie'
57
+ self.cookie_attributes = {}
46
58
  self.bypass_sign_in = true
59
+ self.send_confirmation_email = false
60
+ self.require_client_password_reset_token = false
61
+ self.other_uid = nil
47
62
 
48
63
  def self.setup(&block)
49
64
  yield self
@@ -8,26 +8,31 @@ module ActionDispatch::Routing
8
8
  opts[:skip] ||= []
9
9
 
10
10
  # check for ctrl overrides, fall back to defaults
11
- sessions_ctrl = opts[:controllers][:sessions] || 'devise_token_auth/sessions'
12
- registrations_ctrl = opts[:controllers][:registrations] || 'devise_token_auth/registrations'
13
- passwords_ctrl = opts[:controllers][:passwords] || 'devise_token_auth/passwords'
14
- confirmations_ctrl = opts[:controllers][:confirmations] || 'devise_token_auth/confirmations'
15
- token_validations_ctrl = opts[:controllers][:token_validations] || 'devise_token_auth/token_validations'
16
- omniauth_ctrl = opts[:controllers][:omniauth_callbacks] || 'devise_token_auth/omniauth_callbacks'
17
- unlocks_ctrl = opts[:controllers][:unlocks] || 'devise_token_auth/unlocks'
11
+ sessions_ctrl = opts[:controllers].delete(:sessions) || 'devise_token_auth/sessions'
12
+ registrations_ctrl = opts[:controllers].delete(:registrations) || 'devise_token_auth/registrations'
13
+ passwords_ctrl = opts[:controllers].delete(:passwords) || 'devise_token_auth/passwords'
14
+ confirmations_ctrl = opts[:controllers].delete(:confirmations) || 'devise_token_auth/confirmations'
15
+ token_validations_ctrl = opts[:controllers].delete(:token_validations) || 'devise_token_auth/token_validations'
16
+ omniauth_ctrl = opts[:controllers].delete(:omniauth_callbacks) || 'devise_token_auth/omniauth_callbacks'
17
+ unlocks_ctrl = opts[:controllers].delete(:unlocks) || 'devise_token_auth/unlocks'
18
+
19
+ # check for resource override
20
+ route = opts[:as] || resource.pluralize.underscore.gsub('/', '_')
18
21
 
19
22
  # define devise controller mappings
20
- controllers = { sessions: sessions_ctrl,
23
+ controllers = opts[:controllers].merge(
24
+ sessions: sessions_ctrl,
21
25
  registrations: registrations_ctrl,
22
26
  passwords: passwords_ctrl,
23
- confirmations: confirmations_ctrl }
27
+ confirmations: confirmations_ctrl
28
+ )
24
29
 
25
30
  controllers[:unlocks] = unlocks_ctrl if unlocks_ctrl
26
31
 
27
32
  # remove any unwanted devise modules
28
33
  opts[:skip].each{ |item| controllers.delete(item) }
29
34
 
30
- devise_for resource.pluralize.underscore.gsub('/', '_').to_sym,
35
+ devise_for route.to_sym,
31
36
  class_name: resource,
32
37
  module: :devise,
33
38
  path: opts[:at].to_s,
@@ -56,7 +61,7 @@ module ActionDispatch::Routing
56
61
 
57
62
  devise_scope mapping_name.to_sym do
58
63
  # path to verify token validity
59
- get "#{full_path}/validate_token", controller: token_validations_ctrl.to_s, action: 'validate_token'
64
+ get "#{full_path}/validate_token", controller: token_validations_ctrl.to_s, action: 'validate_token' if !opts[:skip].include?(:token_validations)
60
65
 
61
66
  # omniauth routes. only define if omniauth is installed and not skipped.
62
67
  if defined?(::OmniAuth) && !opts[:skip].include?(:omniauth_callbacks)
@@ -68,7 +73,7 @@ module ActionDispatch::Routing
68
73
 
69
74
  # preserve the resource class thru oauth authentication by setting name of
70
75
  # resource as "resource_class" param
71
- match "#{full_path}/:provider", to: redirect{ |params, request|
76
+ match "#{full_path}/:provider", to: redirect(status: 307) { |params, request|
72
77
  # get the current querystring
73
78
  qs = CGI::parse(request.env['QUERY_STRING'])
74
79
 
@@ -94,7 +99,7 @@ module ActionDispatch::Routing
94
99
 
95
100
  # re-construct the path for omniauth
96
101
  "#{::OmniAuth.config.path_prefix}/#{params[:provider]}?#{redirect_params.to_param}"
97
- }, via: [:get]
102
+ }, via: [:get, :post]
98
103
  end
99
104
  end
100
105
  end
@@ -0,0 +1,126 @@
1
+ require 'bcrypt'
2
+
3
+ module DeviseTokenAuth
4
+ # A token management factory which allow generate token objects and check them.
5
+ module TokenFactory
6
+ # For BCrypt::Password class see:
7
+ # https://github.com/codahale/bcrypt-ruby/blob/master/lib/bcrypt/password.rb
8
+
9
+ # Creates a token instance. Takes an optional client, lifespan and cost options.
10
+ # Example:
11
+ # DeviseTokenAuth::TokenFactory.create
12
+ # => #<struct DeviseTokenAuth::TokenFactory::Token client="tElcgkdZ7f9XEa0unZhrYQ", token="rAMcWOs0-mGHFMnIgJD2cA", token_hash="$2a$10$wrsdlHVRGlYW11wfImxU..jr0Ux3bHo/qbXcSfgp8zmvVUNHosita", expiry=1518982690>
13
+ #
14
+ # DeviseTokenAuth::TokenFactory.create(lifespan: 10, cost: 4)
15
+ # => #<struct DeviseTokenAuth::TokenFactory::Token client="5qleT7_t9JPVcX9xmxkVYA", token="RBXX43u4xXNSO-fr2N_4pA", token_hash="$2a$04$9gpCaoFbu2dUKxU3qiTgluHX7jj9UzS.jq1QW0EkQmoaxARo1WxTy", expiry=1517773268>
16
+ def self.create(client: nil, lifespan: nil, cost: nil)
17
+ # obj_client = client.nil? ? client() : client
18
+ obj_client = client || client()
19
+ obj_token = token
20
+ obj_token_hash = token_hash(obj_token, cost)
21
+ obj_expiry = expiry(lifespan)
22
+
23
+ Token.new(obj_client, obj_token, obj_token_hash, obj_expiry)
24
+ end
25
+
26
+ # Generates a random URL-safe client.
27
+ # Example:
28
+ # DeviseTokenAuth::TokenFactory.client
29
+ # => "zNf0pNP5iGfuBItZJGCseQ"
30
+ def self.client
31
+ secure_string
32
+ end
33
+
34
+ # Generates a random URL-safe token.
35
+ # Example:
36
+ # DeviseTokenAuth::TokenFactory.token
37
+ # => "6Bqs4K9x8ChLmZogvruF3A"
38
+ def self.token
39
+ secure_string
40
+ end
41
+
42
+ # Returns token hash for a token with given cost. If no cost value is specified,
43
+ # the default value is used. The possible cost value is within range from 4 to 31.
44
+ # It is recommended to not use a value more than 10.
45
+ # Example:
46
+ # DeviseTokenAuth::TokenFactory.token_hash("_qxAxmc-biQLiYRHsmwd5Q")
47
+ # => "$2a$10$6/cTAtQ3CBLfpkeHW7dlt.PD2aVCbFRN5vDDJUUhGsZ6pzYFlh4Me"
48
+ #
49
+ # DeviseTokenAuth::TokenFactory.token_hash("_qxAxmc-biQLiYRHsmwd5Q", 4)
50
+ # => "$2a$04$RkIrosbdRtuet2eUk3si8eS4ufeNpiPc/rSSsfpniRK8ogM5YFOWS"
51
+ def self.token_hash(token, cost = nil)
52
+ cost ||= DeviseTokenAuth.token_cost
53
+ BCrypt::Password.create(token, cost: cost)
54
+ end
55
+
56
+ # Returns the value of time as an integer number of seconds. Takes one argument.
57
+ # Example:
58
+ # DeviseTokenAuth::TokenFactory.expiry
59
+ # => 1518983359
60
+ # DeviseTokenAuth::TokenFactory.expiry(10)
61
+ # => 1517773781
62
+ def self.expiry(lifespan = nil)
63
+ lifespan ||= DeviseTokenAuth.token_lifespan
64
+ (Time.zone.now + lifespan).to_i
65
+ end
66
+
67
+ # Generates a random URL-safe string.
68
+ # Example:
69
+ # DeviseTokenAuth::TokenFactory.secure_string
70
+ # => "ADBoIaqXsEDnxIpOuumrTA"
71
+ def self.secure_string
72
+ # https://ruby-doc.org/stdlib-2.5.0/libdoc/securerandom/rdoc/Random/Formatter.html#method-i-urlsafe_base64
73
+ SecureRandom.urlsafe_base64
74
+ end
75
+
76
+ # Returns true if token hash is a valid token hash.
77
+ # Example:
78
+ # token_hash = "$2a$10$ArjX0tskRIa5Z/Tmapy59OCiAXLStfhrCiaDz.8fCb6hnX1gJ0p/2"
79
+ # DeviseTokenAuth::TokenFactory.valid_token_hash?(token_hash)
80
+ # => true
81
+ def self.valid_token_hash?(token_hash)
82
+ !!BCrypt::Password.valid_hash?(token_hash)
83
+ end
84
+
85
+ # Compares a potential token against the token hash. Returns true if the token is the original token, false otherwise.
86
+ # Example:
87
+ # token = "4wZ9gcc900rMQD1McpcSNA"
88
+ # token_hash = "$2a$10$ArjX0tskRIa5Z/Tmapy59OCiAXLStfhrCiaDz.8fCb6hnX1gJ0p/2"
89
+ # DeviseTokenAuth::TokenFactory.token_hash_is_token?(token_hash, token)
90
+ # => true
91
+ def self.token_hash_is_token?(token_hash, token)
92
+ BCrypt::Password.new(token_hash).is_password?(token)
93
+ rescue StandardError
94
+ false
95
+ end
96
+
97
+ # Creates a token instance with instance variables equal nil.
98
+ # Example:
99
+ # DeviseTokenAuth::TokenFactory.new
100
+ # => #<struct DeviseTokenAuth::TokenFactory::Token client=nil, token=nil, token_hash=nil, expiry=nil>
101
+ def self.new
102
+ Token.new
103
+ end
104
+
105
+ Token = Struct.new(:client, :token, :token_hash, :expiry) do
106
+ # Sets all instance variables of the token to nil. It is faster than creating new empty token.
107
+ # Example:
108
+ # token.clear!
109
+ # => true
110
+ # token
111
+ # => #<struct DeviseTokenAuth::TokenFactory::Token client=nil, token=nil, token_hash=nil, expiry=nil>
112
+ def clear!
113
+ size.times { |i| self[i] = nil }
114
+ true
115
+ end
116
+
117
+ # Checks token attribute presence
118
+ # Example:
119
+ # token.present?
120
+ # => true
121
+ def present?
122
+ token.present?
123
+ end
124
+ end
125
+ end
126
+ end
@@ -11,6 +11,9 @@ module DeviseTokenAuth::Url
11
11
  query = [uri.query, params.to_query].reject(&:blank?).join('&')
12
12
  res += "?#{query}"
13
13
  res += "##{uri.fragment}" if uri.fragment
14
+ # repeat any query params after the fragment to deal with Angular eating any pre fragment query params, used
15
+ # in the reset password redirect url
16
+ res += "?#{query}" if uri.fragment
14
17
 
15
18
  res
16
19
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeviseTokenAuth
4
- VERSION = '1.0.0'.freeze
4
+ VERSION = '1.2.1'.freeze
5
5
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'devise'
4
+
5
+ module DeviseTokenAuth
6
+ end
7
+
4
8
  require 'devise_token_auth/engine'
5
9
  require 'devise_token_auth/controllers/helpers'
6
10
  require 'devise_token_auth/controllers/url_helpers'
7
11
  require 'devise_token_auth/url'
8
12
  require 'devise_token_auth/errors'
9
-
10
- module DeviseTokenAuth
11
- end
13
+ require 'devise_token_auth/blacklist'
14
+ require 'devise_token_auth/token_factory'
@@ -8,7 +8,7 @@ Arguments:
8
8
  # 'User'
9
9
  MOUNT_PATH # The path at which to mount the authentication routes. Default is
10
10
  # 'auth'. More detail documentation is here:
11
- # https://github.com/lynndylanhurley/devise_token_auth#usage-tldr
11
+ # https://devise-token-auth.gitbook.io/devise-token-auth/usage
12
12
 
13
13
  Example:
14
14
  rails generate devise_token_auth:install User auth
@@ -1,20 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'install_generator_helpers'
4
+
3
5
  module DeviseTokenAuth
4
6
  class InstallGenerator < Rails::Generators::Base
5
7
  include Rails::Generators::Migration
8
+ include DeviseTokenAuth::InstallGeneratorHelpers
6
9
 
7
10
  class_option :primary_key_type, type: :string, desc: 'The type for primary key'
8
11
 
9
- source_root File.expand_path('templates', __dir__)
10
-
11
- argument :user_class, type: :string, default: 'User'
12
- argument :mount_path, type: :string, default: 'auth'
13
-
14
- def create_initializer_file
15
- copy_file('devise_token_auth.rb', 'config/initializers/devise_token_auth.rb')
16
- end
17
-
18
12
  def copy_migrations
19
13
  if self.class.migration_exists?('db/migrate', "devise_token_auth_create_#{user_class.pluralize.gsub('::','').underscore}")
20
14
  say_status('skipped', "Migration 'devise_token_auth_create_#{user_class.pluralize.gsub('::','').underscore}' already exists")
@@ -32,7 +26,7 @@ module DeviseTokenAuth
32
26
  inclusion = 'include DeviseTokenAuth::Concerns::User'
33
27
  unless parse_file_for_line(fname, inclusion)
34
28
 
35
- active_record_needle = (Rails::VERSION::MAJOR == 5) ? 'ApplicationRecord' : 'ActiveRecord::Base'
29
+ active_record_needle = (Rails::VERSION::MAJOR >= 5) ? 'ApplicationRecord' : 'ActiveRecord::Base'
36
30
  inject_into_file fname, after: "class #{user_class} < #{active_record_needle}\n" do <<-'RUBY'
37
31
  # Include default devise modules.
38
32
  devise :database_authenticatable, :registerable,
@@ -47,90 +41,12 @@ module DeviseTokenAuth
47
41
  end
48
42
  end
49
43
 
50
- def include_controller_concerns
51
- fname = 'app/controllers/application_controller.rb'
52
- line = 'include DeviseTokenAuth::Concerns::SetUserByToken'
53
-
54
- if File.exist?(File.join(destination_root, fname))
55
- if parse_file_for_line(fname, line)
56
- say_status('skipped', 'Concern is already included in the application controller.')
57
- elsif is_rails_api?
58
- inject_into_file fname, after: "class ApplicationController < ActionController::API\n" do <<-'RUBY'
59
- include DeviseTokenAuth::Concerns::SetUserByToken
60
- RUBY
61
- end
62
- else
63
- inject_into_file fname, after: "class ApplicationController < ActionController::Base\n" do <<-'RUBY'
64
- include DeviseTokenAuth::Concerns::SetUserByToken
65
- RUBY
66
- end
67
- end
68
- else
69
- say_status('skipped', "app/controllers/application_controller.rb not found. Add 'include DeviseTokenAuth::Concerns::SetUserByToken' to any controllers that require authentication.")
70
- end
71
- end
72
-
73
- def add_route_mount
74
- f = 'config/routes.rb'
75
- str = "mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'"
76
-
77
- if File.exist?(File.join(destination_root, f))
78
- line = parse_file_for_line(f, 'mount_devise_token_auth_for')
79
-
80
- if line
81
- existing_user_class = true
82
- else
83
- line = 'Rails.application.routes.draw do'
84
- existing_user_class = false
85
- end
86
-
87
- if parse_file_for_line(f, str)
88
- say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
89
- else
90
- insert_after_line(f, line, str)
91
-
92
- if existing_user_class
93
- scoped_routes = ''\
94
- "as :#{user_class.underscore} do\n"\
95
- " # Define routes for #{user_class} within this block.\n"\
96
- " end\n"
97
- insert_after_line(f, str, scoped_routes)
98
- end
99
- end
100
- else
101
- say_status('skipped', "config/routes.rb not found. Add \"mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'\" to your routes file.")
102
- end
103
- end
104
-
105
44
  private
106
45
 
107
46
  def self.next_migration_number(path)
108
47
  Time.zone.now.utc.strftime('%Y%m%d%H%M%S')
109
48
  end
110
49
 
111
- def insert_after_line(filename, line, str)
112
- gsub_file filename, /(#{Regexp.escape(line)})/mi do |match|
113
- "#{match}\n #{str}"
114
- end
115
- end
116
-
117
- def parse_file_for_line(filename, str)
118
- match = false
119
-
120
- File.open(File.join(destination_root, filename)) do |f|
121
- f.each_line do |line|
122
- match = line if line =~ /(#{Regexp.escape(str)})/mi
123
- end
124
- end
125
- match
126
- end
127
-
128
- def is_rails_api?
129
- fname = 'app/controllers/application_controller.rb'
130
- line = 'class ApplicationController < ActionController::API'
131
- parse_file_for_line(fname, line)
132
- end
133
-
134
50
  def json_supported_database?
135
51
  (postgres? && postgres_correct_version?) || (mysql? && mysql_correct_version?)
136
52
  end
@@ -159,12 +75,12 @@ module DeviseTokenAuth
159
75
  ActiveRecord::Base.connection.select_value('SELECT VERSION()')
160
76
  end
161
77
 
162
- def rails5?
163
- Rails.version.start_with? '5'
78
+ def rails_5_or_newer?
79
+ Rails::VERSION::MAJOR >= 5
164
80
  end
165
81
 
166
82
  def primary_key_type
167
- primary_key_string if rails5?
83
+ primary_key_string if rails_5_or_newer?
168
84
  end
169
85
 
170
86
  def primary_key_string
@@ -0,0 +1,98 @@
1
+ module DeviseTokenAuth
2
+ module InstallGeneratorHelpers
3
+ class << self
4
+ def included(mod)
5
+ mod.class_eval do
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ argument :user_class, type: :string, default: 'User'
9
+ argument :mount_path, type: :string, default: 'auth'
10
+
11
+ def create_initializer_file
12
+ copy_file('devise_token_auth.rb', 'config/initializers/devise_token_auth.rb')
13
+ end
14
+
15
+ def include_controller_concerns
16
+ fname = 'app/controllers/application_controller.rb'
17
+ line = 'include DeviseTokenAuth::Concerns::SetUserByToken'
18
+
19
+ if File.exist?(File.join(destination_root, fname))
20
+ if parse_file_for_line(fname, line)
21
+ say_status('skipped', 'Concern is already included in the application controller.')
22
+ elsif is_rails_api?
23
+ inject_into_file fname, after: "class ApplicationController < ActionController::API\n" do <<-'RUBY'
24
+ include DeviseTokenAuth::Concerns::SetUserByToken
25
+ RUBY
26
+ end
27
+ else
28
+ inject_into_file fname, after: "class ApplicationController < ActionController::Base\n" do <<-'RUBY'
29
+ include DeviseTokenAuth::Concerns::SetUserByToken
30
+ RUBY
31
+ end
32
+ end
33
+ else
34
+ say_status('skipped', "app/controllers/application_controller.rb not found. Add 'include DeviseTokenAuth::Concerns::SetUserByToken' to any controllers that require authentication.")
35
+ end
36
+ end
37
+
38
+ def add_route_mount
39
+ f = 'config/routes.rb'
40
+ str = "mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'"
41
+
42
+ if File.exist?(File.join(destination_root, f))
43
+ line = parse_file_for_line(f, 'mount_devise_token_auth_for')
44
+
45
+ if line
46
+ existing_user_class = true
47
+ else
48
+ line = 'Rails.application.routes.draw do'
49
+ existing_user_class = false
50
+ end
51
+
52
+ if parse_file_for_line(f, str)
53
+ say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
54
+ else
55
+ insert_after_line(f, line, str)
56
+
57
+ if existing_user_class
58
+ scoped_routes = ''\
59
+ "as :#{user_class.underscore} do\n"\
60
+ " # Define routes for #{user_class} within this block.\n"\
61
+ " end\n"
62
+ insert_after_line(f, str, scoped_routes)
63
+ end
64
+ end
65
+ else
66
+ say_status('skipped', "config/routes.rb not found. Add \"mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'\" to your routes file.")
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def insert_after_line(filename, line, str)
73
+ gsub_file filename, /(#{Regexp.escape(line)})/mi do |match|
74
+ "#{match}\n #{str}"
75
+ end
76
+ end
77
+
78
+ def parse_file_for_line(filename, str)
79
+ match = false
80
+
81
+ File.open(File.join(destination_root, filename)) do |f|
82
+ f.each_line do |line|
83
+ match = line if line =~ /(#{Regexp.escape(str)})/mi
84
+ end
85
+ end
86
+ match
87
+ end
88
+
89
+ def is_rails_api?
90
+ fname = 'app/controllers/application_controller.rb'
91
+ line = 'class ApplicationController < ActionController::API'
92
+ parse_file_for_line(fname, line)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end