digix_devise_token_auth 0.1.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +952 -0
- data/Rakefile +35 -0
- data/app/controllers/devise_token_auth/application_controller.rb +76 -0
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +43 -0
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +165 -0
- data/app/controllers/devise_token_auth/confirmations_controller.rb +30 -0
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +243 -0
- data/app/controllers/devise_token_auth/passwords_controller.rb +202 -0
- data/app/controllers/devise_token_auth/registrations_controller.rb +205 -0
- data/app/controllers/devise_token_auth/sessions_controller.rb +133 -0
- data/app/controllers/devise_token_auth/token_validations_controller.rb +29 -0
- data/app/controllers/devise_token_auth/unlocks_controller.rb +89 -0
- data/app/models/devise_token_auth/concerns/user.rb +260 -0
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +26 -0
- data/app/validators/email_validator.rb +21 -0
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
- data/config/initializers/devise.rb +196 -0
- data/config/locales/da-DK.yml +50 -0
- data/config/locales/de.yml +49 -0
- data/config/locales/en.yml +50 -0
- data/config/locales/es.yml +49 -0
- data/config/locales/fr.yml +49 -0
- data/config/locales/it.yml +46 -0
- data/config/locales/ja.yml +46 -0
- data/config/locales/nl.yml +30 -0
- data/config/locales/pl.yml +48 -0
- data/config/locales/pt-BR.yml +46 -0
- data/config/locales/pt.yml +48 -0
- data/config/locales/ro.yml +46 -0
- data/config/locales/ru.yml +50 -0
- data/config/locales/sq.yml +46 -0
- data/config/locales/uk.yml +59 -0
- data/config/locales/vi.yml +50 -0
- data/config/locales/zh-CN.yml +46 -0
- data/config/locales/zh-HK.yml +48 -0
- data/config/locales/zh-TW.yml +48 -0
- data/lib/devise_token_auth.rb +8 -0
- data/lib/devise_token_auth/controllers/helpers.rb +149 -0
- data/lib/devise_token_auth/controllers/url_helpers.rb +8 -0
- data/lib/devise_token_auth/engine.rb +90 -0
- data/lib/devise_token_auth/rails/routes.rb +114 -0
- data/lib/devise_token_auth/url.rb +37 -0
- data/lib/devise_token_auth/version.rb +3 -0
- data/lib/generators/devise_token_auth/USAGE +31 -0
- data/lib/generators/devise_token_auth/install_generator.rb +160 -0
- data/lib/generators/devise_token_auth/install_views_generator.rb +16 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +48 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +55 -0
- data/lib/generators/devise_token_auth/templates/user.rb +7 -0
- data/lib/tasks/devise_token_auth_tasks.rake +4 -0
- data/test/controllers/custom/custom_confirmations_controller_test.rb +21 -0
- data/test/controllers/custom/custom_omniauth_callbacks_controller_test.rb +29 -0
- data/test/controllers/custom/custom_passwords_controller_test.rb +75 -0
- data/test/controllers/custom/custom_registrations_controller_test.rb +54 -0
- data/test/controllers/custom/custom_sessions_controller_test.rb +37 -0
- data/test/controllers/custom/custom_token_validations_controller_test.rb +40 -0
- data/test/controllers/demo_group_controller_test.rb +153 -0
- data/test/controllers/demo_mang_controller_test.rb +284 -0
- data/test/controllers/demo_user_controller_test.rb +601 -0
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +129 -0
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +371 -0
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +649 -0
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +878 -0
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +500 -0
- data/test/controllers/devise_token_auth/token_validations_controller_test.rb +90 -0
- data/test/controllers/devise_token_auth/unlocks_controller_test.rb +194 -0
- data/test/controllers/overrides/confirmations_controller_test.rb +43 -0
- data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +49 -0
- data/test/controllers/overrides/passwords_controller_test.rb +66 -0
- data/test/controllers/overrides/registrations_controller_test.rb +40 -0
- data/test/controllers/overrides/sessions_controller_test.rb +33 -0
- data/test/controllers/overrides/token_validations_controller_test.rb +41 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/app/controllers/application_controller.rb +16 -0
- data/test/dummy/app/controllers/auth_origin_controller.rb +5 -0
- data/test/dummy/app/controllers/custom/confirmations_controller.rb +13 -0
- data/test/dummy/app/controllers/custom/omniauth_callbacks_controller.rb +11 -0
- data/test/dummy/app/controllers/custom/passwords_controller.rb +40 -0
- data/test/dummy/app/controllers/custom/registrations_controller.rb +39 -0
- data/test/dummy/app/controllers/custom/sessions_controller.rb +29 -0
- data/test/dummy/app/controllers/custom/token_validations_controller.rb +19 -0
- data/test/dummy/app/controllers/demo_group_controller.rb +13 -0
- data/test/dummy/app/controllers/demo_mang_controller.rb +12 -0
- data/test/dummy/app/controllers/demo_user_controller.rb +25 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +26 -0
- data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +14 -0
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +33 -0
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +27 -0
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +36 -0
- data/test/dummy/app/controllers/overrides/token_validations_controller.rb +23 -0
- data/test/dummy/app/helpers/application_helper.rb +1065 -0
- data/test/dummy/app/models/evil_user.rb +3 -0
- data/test/dummy/app/models/lockable_user.rb +5 -0
- data/test/dummy/app/models/mang.rb +3 -0
- data/test/dummy/app/models/nice_user.rb +7 -0
- data/test/dummy/app/models/only_email_user.rb +5 -0
- data/test/dummy/app/models/scoped_user.rb +7 -0
- data/test/dummy/app/models/unconfirmable_user.rb +8 -0
- data/test/dummy/app/models/unregisterable_user.rb +7 -0
- data/test/dummy/app/models/user.rb +18 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +16 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/application.yml.bk +0 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +44 -0
- data/test/dummy/config/environments/production.rb +82 -0
- data/test/dummy/config/environments/test.rb +48 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/devise.rb +3 -0
- data/test/dummy/config/initializers/devise_token_auth.rb +22 -0
- data/test/dummy/config/initializers/figaro.rb +1 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/omniauth.rb +8 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/routes.rb +72 -0
- data/test/dummy/config/spring.rb +1 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +63 -0
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +62 -0
- data/test/dummy/db/migrate/20140829044006_add_operating_thetan_to_user.rb +6 -0
- data/test/dummy/db/migrate/20140916224624_add_favorite_color_to_mangs.rb +5 -0
- data/test/dummy/db/migrate/20140928231203_devise_token_auth_create_evil_users.rb +64 -0
- data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +60 -0
- data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +61 -0
- data/test/dummy/db/migrate/20150409095712_devise_token_auth_create_nice_users.rb +61 -0
- data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +61 -0
- data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +61 -0
- data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +61 -0
- data/test/dummy/db/schema.rb +258 -0
- data/test/dummy/lib/migration_database_helper.rb +29 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/lib/devise_token_auth/url_test.rb +24 -0
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +187 -0
- data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +23 -0
- data/test/models/only_email_user_test.rb +35 -0
- data/test/models/user_test.rb +169 -0
- data/test/test_helper.rb +77 -0
- metadata +342 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class TokenValidationsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
skip_before_action :assert_is_devise_resource!, :only => [:validate_token]
|
|
4
|
+
before_action :set_user_by_token, :only => [:validate_token]
|
|
5
|
+
|
|
6
|
+
def validate_token
|
|
7
|
+
# @resource will have been set by set_user_by_token concern
|
|
8
|
+
if @resource
|
|
9
|
+
yield @resource if block_given?
|
|
10
|
+
render_validate_token_success
|
|
11
|
+
else
|
|
12
|
+
render_validate_token_error
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
|
|
18
|
+
def render_validate_token_success
|
|
19
|
+
render json: {
|
|
20
|
+
success: true,
|
|
21
|
+
data: resource_data(resource_json: @resource.token_validation_response)
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render_validate_token_error
|
|
26
|
+
render_error(401, I18n.t("devise_token_auth.token_validations.invalid"))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class UnlocksController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
skip_after_action :update_auth_header, :only => [:create, :show]
|
|
4
|
+
|
|
5
|
+
# this action is responsible for generating unlock tokens and
|
|
6
|
+
# sending emails
|
|
7
|
+
def create
|
|
8
|
+
unless resource_params[:email]
|
|
9
|
+
return render_create_error_missing_email
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@email = get_case_insensitive_field_from_resource_params(:email)
|
|
13
|
+
@resource = find_resource(:email, @email)
|
|
14
|
+
|
|
15
|
+
if @resource
|
|
16
|
+
yield @resource if block_given?
|
|
17
|
+
|
|
18
|
+
@resource.send_unlock_instructions({
|
|
19
|
+
email: @email,
|
|
20
|
+
provider: 'email',
|
|
21
|
+
client_config: params[:config_name]
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if @resource.errors.empty?
|
|
25
|
+
return render_create_success
|
|
26
|
+
else
|
|
27
|
+
render_create_error @resource.errors
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
render_not_found_error
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def show
|
|
35
|
+
@resource = resource_class.unlock_access_by_token(params[:unlock_token])
|
|
36
|
+
|
|
37
|
+
if @resource && @resource.id
|
|
38
|
+
client_id, token = @resource.create_token
|
|
39
|
+
@resource.save!
|
|
40
|
+
yield @resource if block_given?
|
|
41
|
+
|
|
42
|
+
redirect_header_options = {unlock: true}
|
|
43
|
+
redirect_headers = build_redirect_headers(token,
|
|
44
|
+
client_id,
|
|
45
|
+
redirect_header_options)
|
|
46
|
+
redirect_to(@resource.build_auth_url(after_unlock_path_for(@resource),
|
|
47
|
+
redirect_headers))
|
|
48
|
+
else
|
|
49
|
+
render_show_error
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def after_unlock_path_for(resource)
|
|
55
|
+
#TODO: This should probably be a configuration option at the very least.
|
|
56
|
+
'/'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render_create_error_missing_email
|
|
60
|
+
render_error(401, I18n.t("devise_token_auth.unlocks.missing_email"))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_create_success
|
|
64
|
+
render json: {
|
|
65
|
+
success: true,
|
|
66
|
+
message: I18n.t("devise_token_auth.unlocks.sended", email: @email)
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def render_create_error(errors)
|
|
71
|
+
render json: {
|
|
72
|
+
success: false,
|
|
73
|
+
errors: errors,
|
|
74
|
+
}, status: 400
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_show_error
|
|
78
|
+
raise ActionController::RoutingError.new('Not Found')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def render_not_found_error
|
|
82
|
+
render_error(404, I18n.t("devise_token_auth.unlocks.user_not_found", email: @email))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def resource_params
|
|
86
|
+
params.permit(:email, :unlock_token, :config)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
require 'bcrypt'
|
|
2
|
+
|
|
3
|
+
module DeviseTokenAuth::Concerns::User
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def self.tokens_match?(token_hash, token)
|
|
7
|
+
@token_equality_cache ||= {}
|
|
8
|
+
|
|
9
|
+
key = "#{token_hash}/#{token}"
|
|
10
|
+
result = @token_equality_cache[key] ||= (::BCrypt::Password.new(token_hash) == token)
|
|
11
|
+
if @token_equality_cache.size > 10000
|
|
12
|
+
@token_equality_cache = {}
|
|
13
|
+
end
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
# Hack to check if devise is already enabled
|
|
19
|
+
unless self.method_defined?(:devise_modules)
|
|
20
|
+
devise :database_authenticatable, :registerable,
|
|
21
|
+
:recoverable, :trackable, :validatable, :confirmable
|
|
22
|
+
else
|
|
23
|
+
self.devise_modules.delete(:omniauthable)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless tokens_has_json_column_type?
|
|
27
|
+
serialize :tokens, JSON
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if DeviseTokenAuth.default_callbacks
|
|
31
|
+
include DeviseTokenAuth::Concerns::UserOmniauthCallbacks
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# can't set default on text fields in mysql, simulate here instead.
|
|
35
|
+
after_save :set_empty_token_hash
|
|
36
|
+
after_initialize :set_empty_token_hash
|
|
37
|
+
|
|
38
|
+
# get rid of dead tokens
|
|
39
|
+
before_save :destroy_expired_tokens
|
|
40
|
+
|
|
41
|
+
# remove old tokens if password has changed
|
|
42
|
+
before_save :remove_tokens_after_password_reset
|
|
43
|
+
|
|
44
|
+
# don't use default devise email validation
|
|
45
|
+
def email_required?; false; end
|
|
46
|
+
def email_changed?; false; end
|
|
47
|
+
def will_save_change_to_email?; false; end
|
|
48
|
+
|
|
49
|
+
def password_required?
|
|
50
|
+
return false unless provider == 'email'
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# override devise method to include additional info as opts hash
|
|
55
|
+
def send_confirmation_instructions(opts={})
|
|
56
|
+
generate_confirmation_token! unless @raw_confirmation_token
|
|
57
|
+
|
|
58
|
+
# fall back to "default" config name
|
|
59
|
+
opts[:client_config] ||= "default"
|
|
60
|
+
opts[:to] = unconfirmed_email if pending_reconfirmation?
|
|
61
|
+
opts[:redirect_url] ||= DeviseTokenAuth.default_confirm_success_url
|
|
62
|
+
|
|
63
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# override devise method to include additional info as opts hash
|
|
67
|
+
def send_reset_password_instructions(opts={})
|
|
68
|
+
token = set_reset_password_token
|
|
69
|
+
|
|
70
|
+
# fall back to "default" config name
|
|
71
|
+
opts[:client_config] ||= "default"
|
|
72
|
+
|
|
73
|
+
send_devise_notification(:reset_password_instructions, token, opts)
|
|
74
|
+
token
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# override devise method to include additional info as opts hash
|
|
78
|
+
def send_unlock_instructions(opts={})
|
|
79
|
+
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
|
80
|
+
self.unlock_token = enc
|
|
81
|
+
save(validate: false)
|
|
82
|
+
|
|
83
|
+
# fall back to "default" config name
|
|
84
|
+
opts[:client_config] ||= "default"
|
|
85
|
+
|
|
86
|
+
send_devise_notification(:unlock_instructions, raw, opts)
|
|
87
|
+
raw
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def create_token(client_id: nil, token: nil, expiry: nil, **token_extras)
|
|
92
|
+
client_id ||= SecureRandom.urlsafe_base64(nil, false)
|
|
93
|
+
token ||= SecureRandom.urlsafe_base64(nil, false)
|
|
94
|
+
expiry ||= (Time.now + token_lifespan).to_i
|
|
95
|
+
|
|
96
|
+
self.tokens[client_id] = {
|
|
97
|
+
token: BCrypt::Password.create(token),
|
|
98
|
+
expiry: expiry
|
|
99
|
+
}.merge!(token_extras)
|
|
100
|
+
|
|
101
|
+
[client_id, token, expiry]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
module ClassMethods
|
|
105
|
+
protected
|
|
106
|
+
|
|
107
|
+
def tokens_has_json_column_type?
|
|
108
|
+
database_exists? && table_exists? && self.columns_hash['tokens'] && self.columns_hash['tokens'].type.in?([:json, :jsonb])
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def database_exists?
|
|
112
|
+
ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def valid_token?(token, client_id='default')
|
|
118
|
+
return false unless tokens[client_id]
|
|
119
|
+
return true if token_is_current?(token, client_id)
|
|
120
|
+
return true if token_can_be_reused?(token, client_id)
|
|
121
|
+
|
|
122
|
+
# return false if none of the above conditions are met
|
|
123
|
+
return false
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# this must be done from the controller so that additional params
|
|
128
|
+
# can be passed on from the client
|
|
129
|
+
def send_confirmation_notification?; false; end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def token_is_current?(token, client_id)
|
|
133
|
+
# ghetto HashWithIndifferentAccess
|
|
134
|
+
expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
|
|
135
|
+
token_hash = tokens[client_id]['token'] || tokens[client_id][:token]
|
|
136
|
+
|
|
137
|
+
return true if (
|
|
138
|
+
# ensure that expiry and token are set
|
|
139
|
+
expiry && token &&
|
|
140
|
+
|
|
141
|
+
# ensure that the token has not yet expired
|
|
142
|
+
DateTime.strptime(expiry.to_s, '%s') > Time.now &&
|
|
143
|
+
|
|
144
|
+
# ensure that the token is valid
|
|
145
|
+
DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# allow batch requests to use the previous token
|
|
151
|
+
def token_can_be_reused?(token, client_id)
|
|
152
|
+
# ghetto HashWithIndifferentAccess
|
|
153
|
+
updated_at = tokens[client_id]['updated_at'] || tokens[client_id][:updated_at]
|
|
154
|
+
last_token = tokens[client_id]['last_token'] || tokens[client_id][:last_token]
|
|
155
|
+
|
|
156
|
+
return true if (
|
|
157
|
+
# ensure that the last token and its creation time exist
|
|
158
|
+
updated_at && last_token &&
|
|
159
|
+
|
|
160
|
+
# ensure that previous token falls within the batch buffer throttle time of the last request
|
|
161
|
+
Time.parse(updated_at) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle &&
|
|
162
|
+
|
|
163
|
+
# ensure that the token is valid
|
|
164
|
+
::BCrypt::Password.new(last_token) == token
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# update user's auth token (should happen on each request)
|
|
170
|
+
def create_new_auth_token(client_id=nil)
|
|
171
|
+
now = Time.now
|
|
172
|
+
|
|
173
|
+
client_id, token = create_token(
|
|
174
|
+
client_id: client_id,
|
|
175
|
+
expiry: (now + token_lifespan).to_i,
|
|
176
|
+
last_token: tokens.fetch(client_id, {})['token'],
|
|
177
|
+
updated_at: now
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
update_auth_header(token, client_id)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def build_auth_header(token, client_id='default')
|
|
184
|
+
# client may use expiry to prevent validation request if expired
|
|
185
|
+
# must be cast as string or headers will break
|
|
186
|
+
expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]
|
|
187
|
+
|
|
188
|
+
{
|
|
189
|
+
DeviseTokenAuth.headers_names[:"access-token"] => token,
|
|
190
|
+
DeviseTokenAuth.headers_names[:"token-type"] => "Bearer",
|
|
191
|
+
DeviseTokenAuth.headers_names[:"client"] => client_id,
|
|
192
|
+
DeviseTokenAuth.headers_names[:"expiry"] => expiry.to_s,
|
|
193
|
+
DeviseTokenAuth.headers_names[:"uid"] => uid
|
|
194
|
+
}
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def update_auth_header(token, client_id='default')
|
|
198
|
+
headers = build_auth_header(token, client_id)
|
|
199
|
+
while tokens.length > 0 && DeviseTokenAuth.max_number_of_devices < tokens.length
|
|
200
|
+
oldest_client_id, _tk = tokens.min_by { |_cid, v| v[:expiry] || v["expiry"] }
|
|
201
|
+
tokens.delete(oldest_client_id)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
save!
|
|
205
|
+
|
|
206
|
+
headers
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def build_auth_url(base_url, args)
|
|
211
|
+
args[:uid] = uid
|
|
212
|
+
args[:expiry] = tokens[args[:client_id]]['expiry']
|
|
213
|
+
|
|
214
|
+
DeviseTokenAuth::Url.generate(base_url, args)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def extend_batch_buffer(token, client_id)
|
|
219
|
+
self.tokens[client_id]['updated_at'] = Time.now
|
|
220
|
+
update_auth_header(token, client_id)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def confirmed?
|
|
224
|
+
devise_modules.exclude?(:confirmable) || super
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def token_validation_response
|
|
228
|
+
as_json(except: [:tokens, :created_at, :updated_at])
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def token_lifespan
|
|
232
|
+
DeviseTokenAuth.token_lifespan
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
protected
|
|
236
|
+
|
|
237
|
+
def set_empty_token_hash
|
|
238
|
+
self.tokens ||= {} if has_attribute?(:tokens)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def destroy_expired_tokens
|
|
242
|
+
if tokens
|
|
243
|
+
tokens.delete_if do |cid, v|
|
|
244
|
+
expiry = v[:expiry] || v["expiry"]
|
|
245
|
+
DateTime.strptime(expiry.to_s, '%s') < Time.now
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def remove_tokens_after_password_reset
|
|
251
|
+
should_remove_old_tokens = DeviseTokenAuth.remove_tokens_after_password_reset &&
|
|
252
|
+
encrypted_password_changed? && tokens && tokens.many?
|
|
253
|
+
|
|
254
|
+
if should_remove_old_tokens
|
|
255
|
+
client_id, token_data = tokens.max_by { |cid, v| v[:expiry] || v["expiry"] }
|
|
256
|
+
self.tokens = {client_id => token_data}
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module DeviseTokenAuth::Concerns::UserOmniauthCallbacks
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
validates :email, presence: true,if: :email_provider?
|
|
6
|
+
validates :email, email: true, allow_nil: true, allow_blank: true, if: :email_provider?
|
|
7
|
+
validates_presence_of :uid, unless: :email_provider?
|
|
8
|
+
|
|
9
|
+
# only validate unique emails among email registration users
|
|
10
|
+
validates :email, uniqueness: { scope: :provider }, on: :create, if: :email_provider?
|
|
11
|
+
|
|
12
|
+
# keep uid in sync with email
|
|
13
|
+
before_save :sync_uid
|
|
14
|
+
before_create :sync_uid
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
def email_provider?
|
|
20
|
+
provider == 'email'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def sync_uid
|
|
24
|
+
self.uid = email if provider == 'email'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class EmailValidator < ActiveModel::EachValidator
|
|
2
|
+
def validate_each(record, attribute, value)
|
|
3
|
+
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
|
|
4
|
+
record.errors[attribute] << email_invalid_message
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def email_invalid_message
|
|
11
|
+
# Try strictly set message:
|
|
12
|
+
message = options[:message]
|
|
13
|
+
|
|
14
|
+
if message.nil?
|
|
15
|
+
# Try DeviceTokenAuth translations or fallback to ActiveModel translations
|
|
16
|
+
message = I18n.t(:'errors.messages.not_email', default: :'errors.messages.invalid')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
message
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<p><%= t(:welcome).capitalize + ' ' + @email %>!</p>
|
|
2
|
+
|
|
3
|
+
<p><%= t '.confirm_link_msg' %> </p>
|
|
4
|
+
|
|
5
|
+
<p><%= link_to t('.confirm_account_link'), confirmation_url(@resource, {confirmation_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url']}).html_safe %></p>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<p><%= t(:hello).capitalize %> <%= @resource.email %>!</p>
|
|
2
|
+
|
|
3
|
+
<p><%= t '.request_reset_link_msg' %></p>
|
|
4
|
+
|
|
5
|
+
<p><%= link_to t('.password_change_link'), edit_password_url(@resource, reset_password_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url'].to_s).html_safe %></p>
|
|
6
|
+
|
|
7
|
+
<p><%= t '.ignore_mail_msg' %></p>
|
|
8
|
+
<p><%= t '.no_changes_msg' %></p>
|