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
data/Rakefile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'DeviseTokenAuth'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'lib'
|
|
28
|
+
t.libs << 'test'
|
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
|
30
|
+
t.verbose = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
task default: :test
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class ApplicationController < DeviseController
|
|
3
|
+
include DeviseTokenAuth::Concerns::SetUserByToken
|
|
4
|
+
respond_to :json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def resource_class(m=nil)
|
|
8
|
+
if m
|
|
9
|
+
mapping = Devise.mappings[m]
|
|
10
|
+
else
|
|
11
|
+
mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
mapping.to
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module DeviseTokenAuth::Concerns::SetUserByToken
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
include DeviseTokenAuth::Controllers::Helpers
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
before_action :set_request_start
|
|
7
|
+
after_action :update_auth_header
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# keep track of request duration
|
|
11
|
+
def set_request_start
|
|
12
|
+
@request_started_at = Time.now
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# user auth
|
|
16
|
+
def set_user_by_token(mapping=nil)
|
|
17
|
+
|
|
18
|
+
# determine target authentication class
|
|
19
|
+
rc = resource_class(mapping)
|
|
20
|
+
rc = rc.active if rc.respond_to?(:active)
|
|
21
|
+
|
|
22
|
+
# no default user defined
|
|
23
|
+
return unless rc
|
|
24
|
+
|
|
25
|
+
# user has already been found and authenticated
|
|
26
|
+
return @resource if @resource and @resource.class == rc
|
|
27
|
+
|
|
28
|
+
# parse header for values necessary for authentication
|
|
29
|
+
uid = request.headers['uid']
|
|
30
|
+
@token = request.headers['access-token']
|
|
31
|
+
@client_id = request.headers['client']
|
|
32
|
+
|
|
33
|
+
return false unless @token
|
|
34
|
+
|
|
35
|
+
# client_id isn't required, set to 'default' if absent
|
|
36
|
+
@client_id ||= 'default'
|
|
37
|
+
|
|
38
|
+
# mitigate timing attacks by finding by uid instead of auth token
|
|
39
|
+
user = uid && rc.find_by_uid(uid)
|
|
40
|
+
|
|
41
|
+
if user && user.valid_token?(@token, @client_id)
|
|
42
|
+
sign_in(:user, user, store: false, bypass: true)
|
|
43
|
+
return @resource = user
|
|
44
|
+
else
|
|
45
|
+
# zero all values previously set values
|
|
46
|
+
return @resource = nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def update_auth_header
|
|
52
|
+
|
|
53
|
+
# cannot save object if model has invalid params
|
|
54
|
+
return unless @resource and @resource.valid? and @client_id
|
|
55
|
+
|
|
56
|
+
# Lock the user record during any auth_header updates to ensure
|
|
57
|
+
# we don't have write contention from multiple threads
|
|
58
|
+
@resource.with_lock do
|
|
59
|
+
|
|
60
|
+
# determine batch request status after request processing, in case
|
|
61
|
+
# another processes has updated it during that processing
|
|
62
|
+
@is_batch_request = is_batch_request?(@resource, @client_id)
|
|
63
|
+
|
|
64
|
+
auth_header = {}
|
|
65
|
+
|
|
66
|
+
if not DeviseTokenAuth.change_headers_on_each_request
|
|
67
|
+
auth_header = @resource.build_auth_header(@token, @client_id)
|
|
68
|
+
return unless auth_header
|
|
69
|
+
|
|
70
|
+
# update the response header
|
|
71
|
+
response.headers.merge!(auth_header)
|
|
72
|
+
|
|
73
|
+
# extend expiration of batch buffer to account for the duration of
|
|
74
|
+
# this request
|
|
75
|
+
elsif @is_batch_request
|
|
76
|
+
auth_header = @resource.extend_batch_buffer(@token, @client_id)
|
|
77
|
+
|
|
78
|
+
# update Authorization response header with new token
|
|
79
|
+
else
|
|
80
|
+
auth_header = @resource.create_new_auth_token(@client_id)
|
|
81
|
+
|
|
82
|
+
# update the response header
|
|
83
|
+
response.headers.merge!(auth_header)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end # end lock
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def resource_class(m=nil)
|
|
91
|
+
if m
|
|
92
|
+
mapping = Devise.mappings[m]
|
|
93
|
+
else
|
|
94
|
+
mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
mapping.to
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def is_batch_request?(user, client_id)
|
|
105
|
+
user.tokens[client_id] and
|
|
106
|
+
user.tokens[client_id]['updated_at'] and
|
|
107
|
+
Time.parse(user.tokens[client_id]['updated_at']) > @request_started_at - DeviseTokenAuth.batch_request_buffer_throttle
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class ConfirmationsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
def show
|
|
4
|
+
@resource = resource_class.confirm_by_token(params[:confirmation_token])
|
|
5
|
+
|
|
6
|
+
if @resource and @resource.id
|
|
7
|
+
# create client id
|
|
8
|
+
client_id = SecureRandom.urlsafe_base64(nil, false)
|
|
9
|
+
token = SecureRandom.urlsafe_base64(nil, false)
|
|
10
|
+
token_hash = BCrypt::Password.create(token)
|
|
11
|
+
expiry = (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
12
|
+
|
|
13
|
+
@resource.tokens[client_id] = {
|
|
14
|
+
token: token_hash,
|
|
15
|
+
expiry: expiry
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@resource.save!
|
|
19
|
+
|
|
20
|
+
redirect_to(@resource.build_auth_url(params[:redirect_url], {
|
|
21
|
+
token: token,
|
|
22
|
+
client_id: client_id,
|
|
23
|
+
account_confirmation_success: true,
|
|
24
|
+
config: params[:config]
|
|
25
|
+
}))
|
|
26
|
+
else
|
|
27
|
+
raise ActionController::RoutingError.new('Not Found')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
skip_before_filter :set_user_by_token
|
|
4
|
+
skip_after_filter :update_auth_header
|
|
5
|
+
|
|
6
|
+
# intermediary route for successful omniauth authentication. omniauth does
|
|
7
|
+
# not support multiple models, so we must resort to this terrible hack.
|
|
8
|
+
def redirect_callbacks
|
|
9
|
+
# derive target redirect route from 'resource_class' param, which was set
|
|
10
|
+
# before authentication.
|
|
11
|
+
devise_mapping = request.env['omniauth.params']['resource_class'].underscore.to_sym
|
|
12
|
+
redirect_route = "#{Devise.mappings[devise_mapping].as_json["path_prefix"]}/#{params[:provider]}/callback"
|
|
13
|
+
|
|
14
|
+
# preserve omniauth info for success route
|
|
15
|
+
session['dta.omniauth.auth'] = request.env['omniauth.auth']
|
|
16
|
+
session['dta.omniauth.params'] = request.env['omniauth.params']
|
|
17
|
+
|
|
18
|
+
redirect_to redirect_route
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def omniauth_success
|
|
22
|
+
# find or create user by provider and provider uid
|
|
23
|
+
@resource = resource_class.where({
|
|
24
|
+
uid: auth_hash['uid'],
|
|
25
|
+
provider: auth_hash['provider']
|
|
26
|
+
}).first_or_initialize
|
|
27
|
+
|
|
28
|
+
# create token info
|
|
29
|
+
@client_id = SecureRandom.urlsafe_base64(nil, false)
|
|
30
|
+
@token = SecureRandom.urlsafe_base64(nil, false)
|
|
31
|
+
@expiry = (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
32
|
+
|
|
33
|
+
@auth_origin_url = generate_url(omniauth_params['auth_origin_url'], {
|
|
34
|
+
token: @token,
|
|
35
|
+
client_id: @client_id,
|
|
36
|
+
uid: @resource.uid,
|
|
37
|
+
expiry: @expiry
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
# set crazy password for new oauth users. this is only used to prevent
|
|
41
|
+
# access via email sign-in.
|
|
42
|
+
unless @resource.id
|
|
43
|
+
p = SecureRandom.urlsafe_base64(nil, false)
|
|
44
|
+
@resource.password = p
|
|
45
|
+
@resource.password_confirmation = p
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@resource.tokens[@client_id] = {
|
|
49
|
+
token: BCrypt::Password.create(@token),
|
|
50
|
+
expiry: @expiry
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# sync user info with provider, update/generate auth token
|
|
54
|
+
assign_provider_attrs(@resource, auth_hash)
|
|
55
|
+
|
|
56
|
+
# assign any additional (whitelisted) attributes
|
|
57
|
+
extra_params = whitelisted_params
|
|
58
|
+
@resource.assign_attributes(extra_params) if extra_params
|
|
59
|
+
|
|
60
|
+
# don't send confirmation email!!!
|
|
61
|
+
@resource.skip_confirmation!
|
|
62
|
+
|
|
63
|
+
sign_in(:user, @resource, store: false, bypass: false)
|
|
64
|
+
|
|
65
|
+
@resource.save!
|
|
66
|
+
|
|
67
|
+
# render user info to javascript postMessage communication window
|
|
68
|
+
respond_to do |format|
|
|
69
|
+
format.html { render :layout => "omniauth_response", :template => "devise_token_auth/omniauth_success" }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# break out provider attribute assignment for easy method extension
|
|
75
|
+
def assign_provider_attrs(user, auth_hash)
|
|
76
|
+
user.assign_attributes({
|
|
77
|
+
nickname: auth_hash['info']['nickname'],
|
|
78
|
+
name: auth_hash['info']['name'],
|
|
79
|
+
image: auth_hash['info']['image'],
|
|
80
|
+
email: auth_hash['info']['email']
|
|
81
|
+
})
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def omniauth_failure
|
|
86
|
+
@error = params[:message]
|
|
87
|
+
|
|
88
|
+
respond_to do |format|
|
|
89
|
+
format.html { render :layout => "omniauth_response", :template => "devise_token_auth/omniauth_failure" }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# derive allowed params from the standard devise parameter sanitizer
|
|
95
|
+
def whitelisted_params
|
|
96
|
+
whitelist = devise_parameter_sanitizer.for(:sign_up)
|
|
97
|
+
|
|
98
|
+
whitelist.inject({}){|coll, key|
|
|
99
|
+
param = omniauth_params[key.to_s]
|
|
100
|
+
if param
|
|
101
|
+
coll[key] = param
|
|
102
|
+
end
|
|
103
|
+
coll
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# pull resource class from omniauth return
|
|
108
|
+
def resource_class
|
|
109
|
+
if omniauth_params
|
|
110
|
+
omniauth_params['resource_class'].constantize
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def resource_name
|
|
115
|
+
resource_class
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# this will be determined differently depending on the action that calls
|
|
119
|
+
# it. redirect_callbacks is called upon returning from successful omniauth
|
|
120
|
+
# authentication, and the target params live in an omniauth-specific
|
|
121
|
+
# request.env variable. this variable is then persisted thru the redirect
|
|
122
|
+
# using our own dta.omniauth.params session var. the omniauth_success
|
|
123
|
+
# method will access that session var and then destroy it immediately
|
|
124
|
+
# after use.
|
|
125
|
+
def omniauth_params
|
|
126
|
+
if request.env['omniauth.params']
|
|
127
|
+
request.env['omniauth.params']
|
|
128
|
+
else
|
|
129
|
+
@_omniauth_params ||= session.delete('dta.omniauth.params')
|
|
130
|
+
@_omniauth_params
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# this sesison value is set by the redirect_callbacks method. its purpose
|
|
135
|
+
# is to persist the omniauth auth hash value thru a redirect. the value
|
|
136
|
+
# must be destroyed immediatly after it is accessed by omniauth_success
|
|
137
|
+
def auth_hash
|
|
138
|
+
@_auth_hash ||= session.delete('dta.omniauth.auth')
|
|
139
|
+
@_auth_hash
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# ensure that this controller responds to :devise_controller? conditionals.
|
|
143
|
+
# this is used primarily for access to the parameter sanitizers.
|
|
144
|
+
def assert_is_devise_resource!
|
|
145
|
+
true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# necessary for access to devise_parameter_sanitizers
|
|
149
|
+
def devise_mapping
|
|
150
|
+
if omniauth_params
|
|
151
|
+
Devise.mappings[omniauth_params['resource_class'].underscore.to_sym]
|
|
152
|
+
else
|
|
153
|
+
request.env['devise.mapping']
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def generate_url(url, params = {})
|
|
158
|
+
auth_url = url
|
|
159
|
+
|
|
160
|
+
# ensure that hash-bang is present BEFORE querystring for angularjs
|
|
161
|
+
unless url.match(/#/)
|
|
162
|
+
auth_url += '#'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# add query AFTER hash-bang
|
|
166
|
+
auth_url += "?#{params.to_query}"
|
|
167
|
+
|
|
168
|
+
return auth_url
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module DeviseTokenAuth
|
|
2
|
+
class PasswordsController < DeviseTokenAuth::ApplicationController
|
|
3
|
+
before_filter :set_user_by_token, :only => [:update]
|
|
4
|
+
skip_after_filter :update_auth_header, :only => [:create, :edit]
|
|
5
|
+
|
|
6
|
+
# this action is responsible for generating password reset tokens and
|
|
7
|
+
# sending emails
|
|
8
|
+
def create
|
|
9
|
+
unless resource_params[:email]
|
|
10
|
+
return render json: {
|
|
11
|
+
success: false,
|
|
12
|
+
errors: ['You must provide an email address.']
|
|
13
|
+
}, status: 401
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
unless params[:redirect_url]
|
|
17
|
+
return render json: {
|
|
18
|
+
success: false,
|
|
19
|
+
errors: ['Missing redirect url.']
|
|
20
|
+
}, status: 401
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# honor devise configuration for case_insensitive_keys
|
|
24
|
+
if resource_class.case_insensitive_keys.include?(:email)
|
|
25
|
+
email = resource_params[:email].downcase
|
|
26
|
+
else
|
|
27
|
+
email = resource_params[:email]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
q = "uid='#{email}' AND provider='email'"
|
|
31
|
+
|
|
32
|
+
# fix for mysql default case insensitivity
|
|
33
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
|
|
34
|
+
q = "BINARY uid='#{email}' AND provider='email'"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@resource = resource_class.where(q).first
|
|
38
|
+
|
|
39
|
+
errors = nil
|
|
40
|
+
|
|
41
|
+
if @resource
|
|
42
|
+
@resource.send_reset_password_instructions({
|
|
43
|
+
email: email,
|
|
44
|
+
provider: 'email',
|
|
45
|
+
redirect_url: params[:redirect_url],
|
|
46
|
+
client_config: params[:config_name]
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if @resource.errors.empty?
|
|
50
|
+
render json: {
|
|
51
|
+
success: true,
|
|
52
|
+
message: "An email has been sent to #{email} containing "+
|
|
53
|
+
"instructions for resetting your password."
|
|
54
|
+
}
|
|
55
|
+
else
|
|
56
|
+
errors = @resource.errors
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
errors = ["Unable to find user with email '#{email}'."]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if errors
|
|
63
|
+
render json: {
|
|
64
|
+
success: false,
|
|
65
|
+
errors: errors
|
|
66
|
+
}, status: 400
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# this is where users arrive after visiting the email confirmation link
|
|
72
|
+
def edit
|
|
73
|
+
@resource = resource_class.reset_password_by_token({
|
|
74
|
+
reset_password_token: resource_params[:reset_password_token]
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if @resource and @resource.id
|
|
78
|
+
client_id = SecureRandom.urlsafe_base64(nil, false)
|
|
79
|
+
token = SecureRandom.urlsafe_base64(nil, false)
|
|
80
|
+
token_hash = BCrypt::Password.create(token)
|
|
81
|
+
expiry = (Time.now + DeviseTokenAuth.token_lifespan).to_i
|
|
82
|
+
|
|
83
|
+
@resource.tokens[client_id] = {
|
|
84
|
+
token: token_hash,
|
|
85
|
+
expiry: expiry
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# ensure that user is confirmed
|
|
89
|
+
@resource.skip_confirmation! unless @resource.confirmed_at
|
|
90
|
+
|
|
91
|
+
@resource.save!
|
|
92
|
+
|
|
93
|
+
redirect_to(@resource.build_auth_url(params[:redirect_url], {
|
|
94
|
+
token: token,
|
|
95
|
+
client_id: client_id,
|
|
96
|
+
reset_password: true,
|
|
97
|
+
config: params[:config]
|
|
98
|
+
}))
|
|
99
|
+
else
|
|
100
|
+
raise ActionController::RoutingError.new('Not Found')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def update
|
|
105
|
+
# make sure user is authorized
|
|
106
|
+
unless @resource
|
|
107
|
+
return render json: {
|
|
108
|
+
success: false,
|
|
109
|
+
errors: ['Unauthorized']
|
|
110
|
+
}, status: 401
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# make sure account doesn't use oauth2 provider
|
|
114
|
+
unless @resource.provider == 'email'
|
|
115
|
+
return render json: {
|
|
116
|
+
success: false,
|
|
117
|
+
errors: ["This account does not require a password. Sign in using "+
|
|
118
|
+
"your #{@resource.provider.humanize} account instead."]
|
|
119
|
+
}, status: 422
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# ensure that password params were sent
|
|
123
|
+
unless password_resource_params[:password] and password_resource_params[:password_confirmation]
|
|
124
|
+
return render json: {
|
|
125
|
+
success: false,
|
|
126
|
+
errors: ['You must fill out the fields labeled "password" and "password confirmation".']
|
|
127
|
+
}, status: 422
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if @resource.update_attributes(password_resource_params)
|
|
131
|
+
return render json: {
|
|
132
|
+
success: true,
|
|
133
|
+
data: {
|
|
134
|
+
user: @resource,
|
|
135
|
+
message: "Your password has been successfully updated."
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
return render json: {
|
|
140
|
+
success: false,
|
|
141
|
+
errors: @resource.errors
|
|
142
|
+
}, status: 422
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def password_resource_params
|
|
147
|
+
params.permit(devise_parameter_sanitizer.for(:account_update))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def resource_params
|
|
151
|
+
params.permit(:email, :password, :password_confirmation, :reset_password_token)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
end
|
|
155
|
+
end
|