cm-devise_token_auth 0.1.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +688 -0
- data/Rakefile +34 -0
- data/app/controllers/devise_token_auth/application_controller.rb +17 -0
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +109 -0
- data/app/controllers/devise_token_auth/confirmations_controller.rb +31 -0
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +171 -0
- data/app/controllers/devise_token_auth/passwords_controller.rb +155 -0
- data/app/controllers/devise_token_auth/registrations_controller.rb +123 -0
- data/app/controllers/devise_token_auth/sessions_controller.rb +98 -0
- data/app/controllers/devise_token_auth/token_validations_controller.rb +23 -0
- data/app/models/devise_token_auth/concerns/user.rb +231 -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_failure.html.erb +2 -0
- data/app/views/devise_token_auth/omniauth_success.html.erb +8 -0
- data/app/views/layouts/omniauth_response.html.erb +31 -0
- data/config/initializers/devise.rb +203 -0
- data/config/locales/devise.en.yml +59 -0
- data/config/routes.rb +5 -0
- data/lib/devise_token_auth.rb +7 -0
- data/lib/devise_token_auth/controllers/helpers.rb +129 -0
- data/lib/devise_token_auth/controllers/url_helpers.rb +8 -0
- data/lib/devise_token_auth/engine.rb +25 -0
- data/lib/devise_token_auth/rails/routes.rb +65 -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 +115 -0
- data/lib/generators/devise_token_auth/install_views_generator.rb +16 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +22 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +54 -0
- data/lib/generators/devise_token_auth/templates/user.rb +3 -0
- data/lib/tasks/devise_token_auth_tasks.rake +4 -0
- data/test/controllers/demo_group_controller_test.rb +126 -0
- data/test/controllers/demo_mang_controller_test.rb +263 -0
- data/test/controllers/demo_user_controller_test.rb +262 -0
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +107 -0
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +167 -0
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +287 -0
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +458 -0
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +221 -0
- data/test/controllers/overrides/confirmations_controller_test.rb +44 -0
- data/test/controllers/overrides/omniauth_callbacks_controller_test.rb +44 -0
- data/test/controllers/overrides/passwords_controller_test.rb +62 -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 +38 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/logo.jpg +0 -0
- data/test/dummy/app/assets/images/omniauth-provider-settings.png +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +16 -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 +12 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +32 -0
- data/test/dummy/app/controllers/overrides/omniauth_callbacks_controller.rb +14 -0
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +39 -0
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +27 -0
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +43 -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/mang.rb +3 -0
- data/test/dummy/app/models/user.rb +18 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +8 -0
- data/test/dummy/bin/rake +8 -0
- data/test/dummy/bin/spring +18 -0
- data/test/dummy/config.ru +16 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/application.yml.bk +0 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +31 -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 +40 -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_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/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +30 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config/spring.rb +1 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +56 -0
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +56 -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 +57 -0
- data/test/dummy/db/schema.rb +114 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fixtures/evil_users.yml +29 -0
- data/test/fixtures/mangs.yml +29 -0
- data/test/fixtures/users.yml +29 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +178 -0
- data/test/lib/generators/devise_token_auth/install_views_generator_test.rb +23 -0
- data/test/models/user_test.rb +90 -0
- data/test/test_helper.rb +60 -0
- metadata +310 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class RegistrationsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
before_filter :set_user_by_token, :only => [:destroy, :update]
|
|
4
|
+
skip_after_filter :update_auth_header, :only => [:create, :destroy]
|
|
5
|
+
|
|
6
|
+
respond_to :json
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
@resource = resource_class.new(sign_up_params)
|
|
10
|
+
@resource.provider = "email"
|
|
11
|
+
|
|
12
|
+
# honor devise configuration for case_insensitive_keys
|
|
13
|
+
if resource_class.case_insensitive_keys.include?(:email)
|
|
14
|
+
@resource.email = sign_up_params[:email].downcase
|
|
15
|
+
else
|
|
16
|
+
@resource.email = sign_up_params[:email]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# success redirect url is required
|
|
20
|
+
unless params[:confirm_success_url]
|
|
21
|
+
return render json: {
|
|
22
|
+
status: 'error',
|
|
23
|
+
data: @resource,
|
|
24
|
+
errors: ["Missing `confirm_success_url` param."]
|
|
25
|
+
}, status: 403
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
# override email confirmation, must be sent manually from ctrl
|
|
30
|
+
resource_class.skip_callback("create", :after, :send_on_create_confirmation_instructions)
|
|
31
|
+
if @resource.save
|
|
32
|
+
|
|
33
|
+
unless @resource.confirmed?
|
|
34
|
+
# user will require email authentication
|
|
35
|
+
@resource.send_confirmation_instructions({
|
|
36
|
+
client_config: params[:config_name],
|
|
37
|
+
redirect_url: params[:confirm_success_url]
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
else
|
|
41
|
+
# email auth has been bypassed, authenticate user
|
|
42
|
+
@client_id = SecureRandom.urlsafe_base64(nil, false)
|
|
43
|
+
@token = SecureRandom.urlsafe_base64(nil, false)
|
|
44
|
+
|
|
45
|
+
@resource.tokens[@client_id] = {
|
|
46
|
+
token: BCrypt::Password.create(@token),
|
|
47
|
+
expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@resource.save!
|
|
51
|
+
|
|
52
|
+
update_auth_header
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
render json: {
|
|
56
|
+
status: 'success',
|
|
57
|
+
data: @resource.as_json
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
clean_up_passwords @resource
|
|
61
|
+
render json: {
|
|
62
|
+
status: 'error',
|
|
63
|
+
data: @resource,
|
|
64
|
+
errors: @resource.errors.to_hash.merge(full_messages: @resource.errors.full_messages)
|
|
65
|
+
}, status: 403
|
|
66
|
+
end
|
|
67
|
+
rescue ActiveRecord::RecordNotUnique
|
|
68
|
+
clean_up_passwords @resource
|
|
69
|
+
render json: {
|
|
70
|
+
status: 'error',
|
|
71
|
+
data: @resource,
|
|
72
|
+
errors: ["An account already exists for #{@resource.email}"]
|
|
73
|
+
}, status: 403
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update
|
|
78
|
+
if @resource
|
|
79
|
+
|
|
80
|
+
if @resource.update_attributes(account_update_params)
|
|
81
|
+
render json: {
|
|
82
|
+
status: 'success',
|
|
83
|
+
data: @resource.as_json
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
render json: {
|
|
87
|
+
status: 'error',
|
|
88
|
+
errors: @resource.errors
|
|
89
|
+
}, status: 403
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
render json: {
|
|
93
|
+
status: 'error',
|
|
94
|
+
errors: ["User not found."]
|
|
95
|
+
}, status: 404
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def destroy
|
|
100
|
+
if @resource
|
|
101
|
+
@resource.destroy
|
|
102
|
+
|
|
103
|
+
render json: {
|
|
104
|
+
status: 'success',
|
|
105
|
+
message: "Account with uid #{@resource.uid} has been destroyed."
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
render json: {
|
|
109
|
+
status: 'error',
|
|
110
|
+
errors: ["Unable to locate account for destruction."]
|
|
111
|
+
}, status: 404
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def sign_up_params
|
|
116
|
+
params.permit(devise_parameter_sanitizer.for(:sign_up))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def account_update_params
|
|
120
|
+
params.permit(devise_parameter_sanitizer.for(:account_update))
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# see http://www.emilsoman.com/blog/2013/05/18/building-a-tested/
|
|
2
|
+
module DeviseTokenAuth
|
|
3
|
+
class SessionsController < DeviseTokenAuth::ApplicationController
|
|
4
|
+
I18N_ERRORS_KEY = "devise_token_auth.errors"
|
|
5
|
+
|
|
6
|
+
before_filter :set_user_by_token, :only => [:destroy]
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
if valid_params?
|
|
10
|
+
# honor devise configuration for case_insensitive_keys
|
|
11
|
+
if resource_class.case_insensitive_keys.include?(:email)
|
|
12
|
+
email = resource_params[:email].downcase
|
|
13
|
+
else
|
|
14
|
+
email = resource_params[:email]
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
render_json_error :unauthorized, :invalid_login, default: "Invalid login credentials. Please try again."
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
q = "uid='#{email}' AND provider='email'"
|
|
22
|
+
|
|
23
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
|
|
24
|
+
q = "BINARY uid='#{email}' AND provider='email'"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
resources = resource_class.where(q)
|
|
28
|
+
resources = resources.active if resource_class.respond_to?(:active)
|
|
29
|
+
@resource = resources.first
|
|
30
|
+
|
|
31
|
+
if @resource and not (!@resource.respond_to?(:active_for_authentication?) or @resource.active_for_authentication?)
|
|
32
|
+
render_json_error :unauthorized, :invalid_login, default: "Invalid login credentials. Please try again."
|
|
33
|
+
|
|
34
|
+
elsif @resource and valid_params? and @resource.valid_password?(resource_params[:password]) and @resource.confirmed?
|
|
35
|
+
# create client id
|
|
36
|
+
@client_id = SecureRandom.urlsafe_base64(nil, false)
|
|
37
|
+
@token = SecureRandom.urlsafe_base64(nil, false)
|
|
38
|
+
|
|
39
|
+
@resource.tokens[@client_id] = {
|
|
40
|
+
token: BCrypt::Password.create(@token),
|
|
41
|
+
expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
42
|
+
}
|
|
43
|
+
@resource.save
|
|
44
|
+
|
|
45
|
+
sign_in(:user, @resource, store: false, bypass: false)
|
|
46
|
+
|
|
47
|
+
render json: {
|
|
48
|
+
data: @resource.as_json(except: [
|
|
49
|
+
:tokens, :created_at, :updated_at
|
|
50
|
+
])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
elsif @resource and not @resource.confirmed?
|
|
54
|
+
default_message = "A confirmation email was sent to your account at #{@resource.email}. "+
|
|
55
|
+
"You must follow the instructions in the email before your account "+
|
|
56
|
+
"can be activated"
|
|
57
|
+
render_json_error :unauthorized, :confirmation_was_sent, default: default_message, email: @resource.email
|
|
58
|
+
|
|
59
|
+
else
|
|
60
|
+
render_json_error :unauthorized, :invalid_login, default: "Invalid login credentials. Please try again."
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def destroy
|
|
65
|
+
# remove auth instance variables so that after_filter does not run
|
|
66
|
+
user = remove_instance_variable(:@resource) if @resource
|
|
67
|
+
client_id = remove_instance_variable(:@client_id) if @client_id
|
|
68
|
+
remove_instance_variable(:@token) if @token
|
|
69
|
+
|
|
70
|
+
if user and client_id and user.tokens[client_id]
|
|
71
|
+
user.tokens.delete(client_id)
|
|
72
|
+
user.save!
|
|
73
|
+
|
|
74
|
+
render json: {
|
|
75
|
+
success:true
|
|
76
|
+
}, status: 200
|
|
77
|
+
|
|
78
|
+
else
|
|
79
|
+
render_json_error :not_found, :user_not_found, default: "User was not found or was not logged in."
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def valid_params?
|
|
84
|
+
resource_params[:password] && resource_params[:email]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def resource_params
|
|
88
|
+
params.permit(devise_parameter_sanitizer.for(:sign_in))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def render_json_error(status, error, options)
|
|
92
|
+
message = I18n.t("#{I18N_ERRORS_KEY}.#{error}", options)
|
|
93
|
+
render json: {
|
|
94
|
+
errors: [message]
|
|
95
|
+
}, status: status
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class TokenValidationsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
skip_before_filter :assert_is_devise_resource!, :only => [:validate_token]
|
|
4
|
+
before_filter :set_user_by_token, :only => [:validate_token]
|
|
5
|
+
|
|
6
|
+
def validate_token
|
|
7
|
+
# @resource will have been set by set_user_token concern
|
|
8
|
+
if @resource
|
|
9
|
+
render json: {
|
|
10
|
+
success: true,
|
|
11
|
+
data: @resource.as_json(except: [
|
|
12
|
+
:tokens, :created_at, :updated_at
|
|
13
|
+
])
|
|
14
|
+
}
|
|
15
|
+
else
|
|
16
|
+
render json: {
|
|
17
|
+
success: false,
|
|
18
|
+
errors: ["Invalid login credentials"]
|
|
19
|
+
}, status: 401
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
module DeviseTokenAuth::Concerns::User
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
# Include default devise modules. Others available are:
|
|
6
|
+
# :confirmable, :lockable, :timeoutable and :omniauthable
|
|
7
|
+
devise :database_authenticatable, :registerable,
|
|
8
|
+
:recoverable, :rememberable, :trackable, :validatable,
|
|
9
|
+
:confirmable
|
|
10
|
+
|
|
11
|
+
serialize :tokens, JSON
|
|
12
|
+
|
|
13
|
+
validates_presence_of :email, if: Proc.new { |u| u.provider == 'email' }
|
|
14
|
+
validates_presence_of :uid, if: Proc.new { |u| u.provider != 'email' }
|
|
15
|
+
|
|
16
|
+
# only validate unique emails among email registration users
|
|
17
|
+
validate :unique_email_user, on: :create
|
|
18
|
+
|
|
19
|
+
# can't set default on text fields in mysql, simulate here instead.
|
|
20
|
+
after_save :set_empty_token_hash
|
|
21
|
+
after_initialize :set_empty_token_hash
|
|
22
|
+
|
|
23
|
+
# keep uid in sync with email
|
|
24
|
+
before_save :sync_uid
|
|
25
|
+
before_create :sync_uid
|
|
26
|
+
|
|
27
|
+
# get rid of dead tokens
|
|
28
|
+
before_save :destroy_expired_tokens
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# don't use default devise email validation
|
|
32
|
+
def email_required?
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def email_changed?
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# override devise method to include additional info as opts hash
|
|
42
|
+
def send_confirmation_instructions(opts=nil)
|
|
43
|
+
unless @raw_confirmation_token
|
|
44
|
+
generate_confirmation_token!
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts ||= {}
|
|
48
|
+
|
|
49
|
+
# fall back to "default" config name
|
|
50
|
+
opts[:client_config] ||= "default"
|
|
51
|
+
|
|
52
|
+
if pending_reconfirmation?
|
|
53
|
+
opts[:to] = unconfirmed_email
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# override devise method to include additional info as opts hash
|
|
60
|
+
def send_reset_password_instructions(opts=nil)
|
|
61
|
+
token = set_reset_password_token
|
|
62
|
+
|
|
63
|
+
opts ||= {}
|
|
64
|
+
|
|
65
|
+
# fall back to "default" config name
|
|
66
|
+
opts[:client_config] ||= "default"
|
|
67
|
+
|
|
68
|
+
if pending_reconfirmation?
|
|
69
|
+
opts[:to] = unconfirmed_email
|
|
70
|
+
else
|
|
71
|
+
opts[:to] = email
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
send_devise_notification(:reset_password_instructions, token, opts)
|
|
75
|
+
|
|
76
|
+
token
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def valid_token?(token, client_id='default')
|
|
82
|
+
client_id ||= 'default'
|
|
83
|
+
|
|
84
|
+
return false unless self.tokens[client_id]
|
|
85
|
+
|
|
86
|
+
return true if token_is_current?(token, client_id)
|
|
87
|
+
return true if token_can_be_reused?(token, client_id)
|
|
88
|
+
|
|
89
|
+
# return false if none of the above conditions are met
|
|
90
|
+
return false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# this must be done from the controller so that additional params
|
|
95
|
+
# can be passed on from the client
|
|
96
|
+
def send_confirmation_notification?
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def token_is_current?(token, client_id)
|
|
102
|
+
return true if (
|
|
103
|
+
# ensure that expiry and token are set
|
|
104
|
+
self.tokens[client_id]['expiry'] and
|
|
105
|
+
self.tokens[client_id]['token'] and
|
|
106
|
+
|
|
107
|
+
# ensure that the token has not yet expired
|
|
108
|
+
DateTime.strptime(self.tokens[client_id]['expiry'].to_s, '%s') > Time.now and
|
|
109
|
+
|
|
110
|
+
# ensure that the token is valid
|
|
111
|
+
BCrypt::Password.new(self.tokens[client_id]['token']) == token
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# allow batch requests to use the previous token
|
|
117
|
+
def token_can_be_reused?(token, client_id)
|
|
118
|
+
return true if (
|
|
119
|
+
# ensure that the last token and its creation time exist
|
|
120
|
+
self.tokens[client_id]['updated_at'] and
|
|
121
|
+
self.tokens[client_id]['last_token'] and
|
|
122
|
+
|
|
123
|
+
# ensure that previous token falls within the batch buffer throttle time of the last request
|
|
124
|
+
Time.parse(self.tokens[client_id]['updated_at']) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle and
|
|
125
|
+
|
|
126
|
+
# ensure that the token is valid
|
|
127
|
+
BCrypt::Password.new(self.tokens[client_id]['last_token']) == token
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# update user's auth token (should happen on each request)
|
|
133
|
+
def create_new_auth_token(client_id=nil)
|
|
134
|
+
client_id ||= SecureRandom.urlsafe_base64(nil, false)
|
|
135
|
+
last_token ||= nil
|
|
136
|
+
token = SecureRandom.urlsafe_base64(nil, false)
|
|
137
|
+
token_hash = BCrypt::Password.create(token)
|
|
138
|
+
expiry = (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
139
|
+
|
|
140
|
+
if self.tokens[client_id] and self.tokens[client_id]['token']
|
|
141
|
+
last_token = self.tokens[client_id]['token']
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
self.tokens[client_id] = {
|
|
145
|
+
token: token_hash,
|
|
146
|
+
expiry: expiry,
|
|
147
|
+
last_token: last_token,
|
|
148
|
+
updated_at: Time.now
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
self.save!
|
|
152
|
+
|
|
153
|
+
return build_auth_header(token, client_id)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def build_auth_header(token, client_id='default')
|
|
158
|
+
return unless self.tokens[client_id]
|
|
159
|
+
|
|
160
|
+
# client may use expiry to prevent validation request if expired
|
|
161
|
+
# must be cast as string or headers will break
|
|
162
|
+
expiry = self.tokens[client_id]['expiry'].to_s
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
"access-token" => token,
|
|
166
|
+
"token-type" => "Bearer",
|
|
167
|
+
"client" => client_id,
|
|
168
|
+
"expiry" => expiry,
|
|
169
|
+
"uid" => self.uid
|
|
170
|
+
}
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def build_auth_url(base_url, args)
|
|
175
|
+
args[:uid] = self.uid
|
|
176
|
+
args[:expiry] = self.tokens[args[:client_id]]['expiry']
|
|
177
|
+
|
|
178
|
+
generate_url(base_url, args)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def extend_batch_buffer(token, client_id)
|
|
183
|
+
self.tokens[client_id]['updated_at'] = Time.now
|
|
184
|
+
self.save!
|
|
185
|
+
|
|
186
|
+
return build_auth_header(token, client_id)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
protected
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# NOTE: ensure that fragment comes AFTER querystring for proper $location
|
|
194
|
+
# parsing using AngularJS.
|
|
195
|
+
def generate_url(url, params = {})
|
|
196
|
+
uri = URI(url)
|
|
197
|
+
|
|
198
|
+
res = "#{uri.scheme}://#{uri.host}"
|
|
199
|
+
res += ":#{uri.port}" if (uri.port and uri.port != 80 and uri.port != 443)
|
|
200
|
+
res += "#{uri.path}" if uri.path
|
|
201
|
+
res += '#'
|
|
202
|
+
res += "#{uri.fragment}" if uri.fragment
|
|
203
|
+
res += "?#{params.to_query}"
|
|
204
|
+
|
|
205
|
+
return res
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# only validate unique email among users that registered by email
|
|
210
|
+
def unique_email_user
|
|
211
|
+
if provider == 'email' and self.class.where(provider: 'email', email: email).count > 0
|
|
212
|
+
errors.add(:email, :already_in_use, default: "This email address is already in use")
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def set_empty_token_hash
|
|
217
|
+
self.tokens ||= {} if has_attribute?(:tokens)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def sync_uid
|
|
221
|
+
self.uid = email if provider == 'email'
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def destroy_expired_tokens
|
|
225
|
+
self.tokens.delete_if{|cid,v|
|
|
226
|
+
expiry = v[:expiry] || v["expiry"]
|
|
227
|
+
DateTime.strptime(expiry.to_s, '%s') < Time.now
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|