devise-tokens 1.0.0 → 1.0.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 +4 -4
- data/app/controllers/devise_tokens/application_controller.rb +77 -0
- data/app/controllers/devise_tokens/concerns/resource_finder.rb +42 -0
- data/app/controllers/devise_tokens/concerns/set_user_by_token.rb +160 -0
- data/app/controllers/devise_tokens/confirmations_controller.rb +79 -0
- data/app/controllers/devise_tokens/omniauth_callbacks_controller.rb +284 -0
- data/app/controllers/devise_tokens/passwords_controller.rb +204 -0
- data/app/controllers/devise_tokens/registrations_controller.rb +203 -0
- data/app/controllers/devise_tokens/sessions_controller.rb +128 -0
- data/app/controllers/devise_tokens/token_validations_controller.rb +29 -0
- data/app/controllers/devise_tokens/unlocks_controller.rb +87 -0
- data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
- data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
- data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
- data/app/models/devise_token_auth/concerns/user.rb +253 -0
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +28 -0
- data/app/validators/devise_token_auth_email_validator.rb +23 -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/locales/da-DK.yml +52 -0
- data/config/locales/de.yml +51 -0
- data/config/locales/en.yml +57 -0
- data/config/locales/es.yml +51 -0
- data/config/locales/fr.yml +51 -0
- data/config/locales/he.yml +52 -0
- data/config/locales/it.yml +48 -0
- data/config/locales/ja.yml +48 -0
- data/config/locales/nl.yml +32 -0
- data/config/locales/pl.yml +50 -0
- data/config/locales/pt-BR.yml +48 -0
- data/config/locales/pt.yml +50 -0
- data/config/locales/ro.yml +48 -0
- data/config/locales/ru.yml +52 -0
- data/config/locales/sq.yml +48 -0
- data/config/locales/sv.yml +52 -0
- data/config/locales/uk.yml +61 -0
- data/config/locales/vi.yml +52 -0
- data/config/locales/zh-CN.yml +48 -0
- data/config/locales/zh-HK.yml +50 -0
- data/config/locales/zh-TW.yml +50 -0
- data/lib/devise_tokens.rb +14 -0
- data/lib/devise_tokens/blacklist.rb +2 -0
- data/lib/devise_tokens/controllers/helpers.rb +161 -0
- data/lib/devise_tokens/controllers/url_helpers.rb +10 -0
- data/lib/devise_tokens/engine.rb +92 -0
- data/lib/devise_tokens/errors.rb +6 -0
- data/lib/devise_tokens/rails/routes.rb +116 -0
- data/lib/devise_tokens/token_factory.rb +126 -0
- data/lib/devise_tokens/url.rb +39 -0
- data/lib/devise_tokens/version.rb +3 -0
- data/lib/generators/devise_tokens/USAGE +31 -0
- data/lib/generators/devise_tokens/install_generator.rb +91 -0
- data/lib/generators/devise_tokens/install_generator_helpers.rb +98 -0
- data/lib/generators/devise_tokens/install_mongoid_generator.rb +46 -0
- data/lib/generators/devise_tokens/install_views_generator.rb +18 -0
- data/lib/generators/devise_tokens/templates/devise_tokens.rb +55 -0
- data/lib/generators/devise_tokens/templates/devise_tokens_create_users.rb.erb +49 -0
- data/lib/generators/devise_tokens/templates/user.rb.erb +9 -0
- data/lib/generators/devise_tokens/templates/user_mongoid.rb.erb +56 -0
- data/lib/tasks/devise_tokens_tasks.rake +6 -0
- metadata +208 -4
- data/lib/devise-tokens.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dafc9b9c1d7eb65cf8162acc3a06dacbd5151dfbea76da7c801564538bd275f
|
4
|
+
data.tar.gz: b798b1450394f2d804bd59efdd273fce5dad770eecf73d017248329314063d41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da07c4adec6ea1aea9f9343f0b508f5125e2922b1c5adff00f2d3a56ffaec3189c4aaa93f6357abd4999a75298bc9227a6a46049a9d0a1e68d4ff0379af89f11
|
7
|
+
data.tar.gz: 6da368d8796a98de5a218a4cf0eaa44b59f0e7466864db2a820b7228f678ec9834b9697cd2c08345018d7110a4d545bccc1fcc0cf190e2f88f0eb25fec07fc39
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module DeviseTokens
|
2
|
+
class ApplicationController < DeviseController
|
3
|
+
include DeviseTokens::Concerns::SetUserByToken
|
4
|
+
|
5
|
+
def resource_data(opts = {})
|
6
|
+
response_data = opts[:resource_json] || @resource.as_json
|
7
|
+
response_data['type'] = @resource.class.name.parameterize if json_api?
|
8
|
+
response_data
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource_errors
|
12
|
+
@resource.errors.to_hash.merge(full_messages: @resource.errors.full_messages)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def blacklisted_redirect_url?(redirect_url)
|
18
|
+
DeviseTokens.redirect_whitelist && !DeviseTokens::Url.whitelisted?(redirect_url)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_redirect_headers(access_token, client, redirect_header_options = {})
|
22
|
+
{
|
23
|
+
DeviseTokens.headers_names[:"access-token"] => access_token,
|
24
|
+
DeviseTokens.headers_names[:"client"] => client,
|
25
|
+
:config => params[:config],
|
26
|
+
|
27
|
+
# Legacy parameters which may be removed in a future release.
|
28
|
+
# Consider using "client" and "access-token" in client code.
|
29
|
+
# See: github.com/lynndylanhurley/devise_tokens/issues/993
|
30
|
+
:client_id => client,
|
31
|
+
:token => access_token
|
32
|
+
}.merge(redirect_header_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def params_for_resource(resource)
|
36
|
+
devise_parameter_sanitizer.instance_values['permitted'][resource].each do |type|
|
37
|
+
params[type.to_s] ||= request.headers[type.to_s] unless request.headers[type.to_s].nil?
|
38
|
+
end
|
39
|
+
devise_parameter_sanitizer.instance_values['permitted'][resource]
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_class(m = nil)
|
43
|
+
if m
|
44
|
+
mapping = Devise.mappings[m]
|
45
|
+
else
|
46
|
+
mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
|
47
|
+
end
|
48
|
+
|
49
|
+
mapping.to
|
50
|
+
end
|
51
|
+
|
52
|
+
def json_api?
|
53
|
+
return false unless defined?(ActiveModel::Serializer)
|
54
|
+
return ActiveModel::Serializer.setup do |config|
|
55
|
+
config.adapter == :json_api
|
56
|
+
end if ActiveModel::Serializer.respond_to?(:setup)
|
57
|
+
ActiveModelSerializers.config.adapter == :json_api
|
58
|
+
end
|
59
|
+
|
60
|
+
def recoverable_enabled?
|
61
|
+
resource_class.devise_modules.include?(:recoverable)
|
62
|
+
end
|
63
|
+
|
64
|
+
def confirmable_enabled?
|
65
|
+
resource_class.devise_modules.include?(:confirmable)
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_error(status, message, data = nil)
|
69
|
+
response = {
|
70
|
+
success: false,
|
71
|
+
errors: [message]
|
72
|
+
}
|
73
|
+
response = response.merge(data) if data
|
74
|
+
render json: response, status: status
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module DeviseTokens::Concerns::ResourceFinder
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
include DeviseTokens::Controllers::Helpers
|
4
|
+
|
5
|
+
def get_case_insensitive_field_from_resource_params(field)
|
6
|
+
# honor Devise configuration for case_insensitive keys
|
7
|
+
q_value = resource_params[field.to_sym]
|
8
|
+
|
9
|
+
if resource_class.case_insensitive_keys.include?(field.to_sym)
|
10
|
+
q_value.downcase!
|
11
|
+
end
|
12
|
+
|
13
|
+
if resource_class.strip_whitespace_keys.include?(field.to_sym)
|
14
|
+
q_value.strip!
|
15
|
+
end
|
16
|
+
|
17
|
+
q_value
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_resource(field, value)
|
21
|
+
@resource = if resource_class.try(:connection_config).try(:[], :adapter).try(:include?, 'mysql')
|
22
|
+
# fix for mysql default case insensitivity
|
23
|
+
resource_class.where("BINARY #{field} = ? AND provider= ?", value, provider).first
|
24
|
+
else
|
25
|
+
resource_class.dta_find_by(field => value, 'provider' => provider)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def resource_class(m = nil)
|
30
|
+
mapping = if m
|
31
|
+
Devise.mappings[m]
|
32
|
+
else
|
33
|
+
Devise.mappings[resource_name] || Devise.mappings.values.first
|
34
|
+
end
|
35
|
+
|
36
|
+
mapping.to
|
37
|
+
end
|
38
|
+
|
39
|
+
def provider
|
40
|
+
'email'
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module DeviseTokens::Concerns::SetUserByToken
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
include DeviseTokens::Concerns::ResourceFinder
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_action :set_request_start
|
7
|
+
after_action :update_auth_header
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# keep track of request duration
|
13
|
+
def set_request_start
|
14
|
+
@request_started_at = Time.zone.now
|
15
|
+
@used_auth_by_token = true
|
16
|
+
|
17
|
+
# initialize instance variables
|
18
|
+
@token = DeviseTokens::TokenFactory.new
|
19
|
+
@resource ||= nil
|
20
|
+
@is_batch_request ||= nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# user auth
|
24
|
+
def set_user_by_token(mapping = nil)
|
25
|
+
# determine target authentication class
|
26
|
+
rc = resource_class(mapping)
|
27
|
+
|
28
|
+
# no default user defined
|
29
|
+
return unless rc
|
30
|
+
|
31
|
+
# gets the headers names, which was set in the initialize file
|
32
|
+
uid_name = DeviseTokens.headers_names[:'uid']
|
33
|
+
access_token_name = DeviseTokens.headers_names[:'access-token']
|
34
|
+
client_name = DeviseTokens.headers_names[:'client']
|
35
|
+
|
36
|
+
# parse header for values necessary for authentication
|
37
|
+
uid = request.headers[uid_name] || params[uid_name]
|
38
|
+
@token = DeviseTokens::TokenFactory.new unless @token
|
39
|
+
@token.token ||= request.headers[access_token_name] || params[access_token_name]
|
40
|
+
@token.client ||= request.headers[client_name] || params[client_name]
|
41
|
+
|
42
|
+
# client isn't required, set to 'default' if absent
|
43
|
+
@token.client ||= 'default'
|
44
|
+
|
45
|
+
# check for an existing user, authenticated via warden/devise, if enabled
|
46
|
+
if DeviseTokens.enable_standard_devise_support
|
47
|
+
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
|
48
|
+
if devise_warden_user && devise_warden_user.tokens[@token.client].nil?
|
49
|
+
@used_auth_by_token = false
|
50
|
+
@resource = devise_warden_user
|
51
|
+
# REVIEW: The following line _should_ be safe to remove;
|
52
|
+
# the generated token does not get used anywhere.
|
53
|
+
# @resource.create_new_auth_token
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# user has already been found and authenticated
|
58
|
+
return @resource if @resource && @resource.is_a?(rc)
|
59
|
+
|
60
|
+
# ensure we clear the client
|
61
|
+
unless @token.present?
|
62
|
+
@token.client = nil
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
# mitigate timing attacks by finding by uid instead of auth token
|
67
|
+
user = uid && rc.dta_find_by(uid: uid)
|
68
|
+
scope = rc.to_s.underscore.to_sym
|
69
|
+
|
70
|
+
if user && user.valid_token?(@token.token, @token.client)
|
71
|
+
# sign_in with bypass: true will be deprecated in the next version of Devise
|
72
|
+
if respond_to?(:bypass_sign_in) && DeviseTokens.bypass_sign_in
|
73
|
+
bypass_sign_in(user, scope: scope)
|
74
|
+
else
|
75
|
+
sign_in(scope, user, store: false, event: :fetch, bypass: DeviseTokens.bypass_sign_in)
|
76
|
+
end
|
77
|
+
return @resource = user
|
78
|
+
else
|
79
|
+
# zero all values previously set values
|
80
|
+
@token.client = nil
|
81
|
+
return @resource = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def update_auth_header
|
86
|
+
# cannot save object if model has invalid params
|
87
|
+
return unless @resource && @token.client
|
88
|
+
|
89
|
+
# Generate new client with existing authentication
|
90
|
+
@token.client = nil unless @used_auth_by_token
|
91
|
+
|
92
|
+
if @used_auth_by_token && !DeviseTokens.change_headers_on_each_request
|
93
|
+
# should not append auth header if @resource related token was
|
94
|
+
# cleared by sign out in the meantime
|
95
|
+
return if @resource.reload.tokens[@token.client].nil?
|
96
|
+
|
97
|
+
auth_header = @resource.build_auth_header(@token.token, @token.client)
|
98
|
+
|
99
|
+
# update the response header
|
100
|
+
response.headers.merge!(auth_header)
|
101
|
+
|
102
|
+
else
|
103
|
+
unless @resource.reload.valid?
|
104
|
+
@resource = resource_class.find(@resource.to_param) # errors remain after reload
|
105
|
+
# if we left the model in a bad state, something is wrong in our app
|
106
|
+
unless @resource.valid?
|
107
|
+
raise DeviseTokens::Errors::InvalidModel, "Cannot set auth token in invalid model. Errors: #{@resource.errors.full_messages}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
refresh_headers
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def refresh_headers
|
117
|
+
# Lock the user record during any auth_header updates to ensure
|
118
|
+
# we don't have write contention from multiple threads
|
119
|
+
@resource.with_lock do
|
120
|
+
# should not append auth header if @resource related token was
|
121
|
+
# cleared by sign out in the meantime
|
122
|
+
return if @used_auth_by_token && @resource.tokens[@token.client].nil?
|
123
|
+
|
124
|
+
# update the response header
|
125
|
+
response.headers.merge!(auth_header_from_batch_request)
|
126
|
+
end # end lock
|
127
|
+
end
|
128
|
+
|
129
|
+
def is_batch_request?(user, client)
|
130
|
+
!params[:unbatch] &&
|
131
|
+
user.tokens[client] &&
|
132
|
+
user.tokens[client]['updated_at'] &&
|
133
|
+
user.tokens[client]['updated_at'].to_time > @request_started_at - DeviseTokens.batch_request_buffer_throttle
|
134
|
+
end
|
135
|
+
|
136
|
+
def auth_header_from_batch_request
|
137
|
+
# determine batch request status after request processing, in case
|
138
|
+
# another processes has updated it during that processing
|
139
|
+
@is_batch_request = is_batch_request?(@resource, @token.client)
|
140
|
+
|
141
|
+
auth_header = {}
|
142
|
+
# extend expiration of batch buffer to account for the duration of
|
143
|
+
# this request
|
144
|
+
if @is_batch_request
|
145
|
+
auth_header = @resource.extend_batch_buffer(@token.token, @token.client)
|
146
|
+
|
147
|
+
# Do not return token for batch requests to avoid invalidated
|
148
|
+
# tokens returned to the client in case of race conditions.
|
149
|
+
# Use a blank string for the header to still be present and
|
150
|
+
# being passed in a XHR response in case of
|
151
|
+
# 304 Not Modified responses.
|
152
|
+
auth_header[DeviseTokens.headers_names[:"access-token"]] = ' '
|
153
|
+
auth_header[DeviseTokens.headers_names[:"expiry"]] = ' '
|
154
|
+
else
|
155
|
+
# update Authorization response header with new token
|
156
|
+
auth_header = @resource.create_new_auth_token(@token.client)
|
157
|
+
end
|
158
|
+
auth_header
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module DeviseTokens
|
2
|
+
class ConfirmationsController < DeviseTokens::ApplicationController
|
3
|
+
|
4
|
+
def show
|
5
|
+
@resource = resource_class.confirm_by_token(resource_params[:confirmation_token])
|
6
|
+
|
7
|
+
if @resource.errors.empty?
|
8
|
+
yield @resource if block_given?
|
9
|
+
|
10
|
+
redirect_header_options = { account_confirmation_success: true }
|
11
|
+
|
12
|
+
if signed_in?(resource_name)
|
13
|
+
token = signed_in_resource.create_token
|
14
|
+
|
15
|
+
redirect_headers = build_redirect_headers(token.token,
|
16
|
+
token.client,
|
17
|
+
redirect_header_options)
|
18
|
+
|
19
|
+
redirect_to_link = signed_in_resource.build_auth_url(redirect_url, redirect_headers)
|
20
|
+
else
|
21
|
+
redirect_to_link = DeviseTokens::Url.generate(redirect_url, redirect_header_options)
|
22
|
+
end
|
23
|
+
|
24
|
+
redirect_to(redirect_to_link)
|
25
|
+
else
|
26
|
+
raise ActionController::RoutingError, 'Not Found'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
return render_create_error_missing_email if resource_params[:email].blank?
|
32
|
+
|
33
|
+
@email = get_case_insensitive_field_from_resource_params(:email)
|
34
|
+
|
35
|
+
@resource = resource_class.dta_find_by(uid: @email, provider: provider)
|
36
|
+
|
37
|
+
return render_not_found_error unless @resource
|
38
|
+
|
39
|
+
@resource.send_confirmation_instructions({
|
40
|
+
redirect_url: redirect_url,
|
41
|
+
client_config: resource_params[:config_name]
|
42
|
+
})
|
43
|
+
|
44
|
+
return render_create_success
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def render_create_error_missing_email
|
50
|
+
render_error(401, I18n.t('devise_tokens.confirmations.missing_email'))
|
51
|
+
end
|
52
|
+
|
53
|
+
def render_create_success
|
54
|
+
render json: {
|
55
|
+
success: true,
|
56
|
+
message: I18n.t('devise_tokens.confirmations.sended', email: @email)
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def render_not_found_error
|
61
|
+
render_error(404, I18n.t('devise_tokens.confirmations.user_not_found', email: @email))
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def resource_params
|
67
|
+
params.permit(:email, :confirmation_token, :config_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
# give redirect value from params priority or fall back to default value if provided
|
71
|
+
def redirect_url
|
72
|
+
params.fetch(
|
73
|
+
:redirect_url,
|
74
|
+
DeviseTokens.default_confirm_success_url
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,284 @@
|
|
1
|
+
module DeviseTokens
|
2
|
+
class OmniauthCallbacksController < DeviseTokens::ApplicationController
|
3
|
+
attr_reader :auth_params
|
4
|
+
|
5
|
+
before_action :validate_auth_origin_url_param
|
6
|
+
|
7
|
+
skip_before_action :set_user_by_token, raise: false
|
8
|
+
skip_after_action :update_auth_header
|
9
|
+
|
10
|
+
# intermediary route for successful omniauth authentication. omniauth does
|
11
|
+
# not support multiple models, so we must resort to this terrible hack.
|
12
|
+
def redirect_callbacks
|
13
|
+
|
14
|
+
# derive target redirect route from 'resource_class' param, which was set
|
15
|
+
# before authentication.
|
16
|
+
devise_mapping = get_devise_mapping
|
17
|
+
redirect_route = get_redirect_route(devise_mapping)
|
18
|
+
|
19
|
+
# preserve omniauth info for success route. ignore 'extra' in twitter
|
20
|
+
# auth response to avoid CookieOverflow.
|
21
|
+
session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
|
22
|
+
session['dta.omniauth.params'] = request.env['omniauth.params']
|
23
|
+
|
24
|
+
redirect_to redirect_route
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_redirect_route(devise_mapping)
|
28
|
+
path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
|
29
|
+
klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
|
30
|
+
redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_devise_mapping
|
34
|
+
# derive target redirect route from 'resource_class' param, which was set
|
35
|
+
# before authentication.
|
36
|
+
devise_mapping = [request.env['omniauth.params']['namespace_name'],
|
37
|
+
request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
|
38
|
+
rescue NoMethodError => err
|
39
|
+
default_devise_mapping
|
40
|
+
end
|
41
|
+
|
42
|
+
# This method will only be called if `get_devise_mapping` cannot
|
43
|
+
# find the mapping in `omniauth.params`.
|
44
|
+
#
|
45
|
+
# One example use-case here is for IDP-initiated SAML login. In that
|
46
|
+
# case, there will have been no initial request in which to save
|
47
|
+
# the devise mapping. If you are in a situation like that, and
|
48
|
+
# your app allows for you to determine somehow what the devise
|
49
|
+
# mapping should be (because, for example, it is always the same),
|
50
|
+
# then you can handle it by overriding this method.
|
51
|
+
def default_devise_mapping
|
52
|
+
raise NotImplementedError.new('no default_devise_mapping set')
|
53
|
+
end
|
54
|
+
|
55
|
+
def omniauth_success
|
56
|
+
get_resource_from_auth_hash
|
57
|
+
set_token_on_resource
|
58
|
+
create_auth_params
|
59
|
+
|
60
|
+
if confirmable_enabled?
|
61
|
+
# don't send confirmation email!!!
|
62
|
+
@resource.skip_confirmation!
|
63
|
+
end
|
64
|
+
|
65
|
+
sign_in(:user, @resource, store: false, bypass: false)
|
66
|
+
|
67
|
+
@resource.save!
|
68
|
+
|
69
|
+
yield @resource if block_given?
|
70
|
+
|
71
|
+
render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
|
72
|
+
end
|
73
|
+
|
74
|
+
def omniauth_failure
|
75
|
+
@error = params[:message]
|
76
|
+
render_data_or_redirect('authFailure', error: @error)
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_auth_origin_url_param
|
80
|
+
return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
# this will be determined differently depending on the action that calls
|
87
|
+
# it. redirect_callbacks is called upon returning from successful omniauth
|
88
|
+
# authentication, and the target params live in an omniauth-specific
|
89
|
+
# request.env variable. this variable is then persisted thru the redirect
|
90
|
+
# using our own dta.omniauth.params session var. the omniauth_success
|
91
|
+
# method will access that session var and then destroy it immediately
|
92
|
+
# after use. In the failure case, finally, the omniauth params
|
93
|
+
# are added as query params in our monkey patch to OmniAuth in engine.rb
|
94
|
+
def omniauth_params
|
95
|
+
unless defined?(@_omniauth_params)
|
96
|
+
if request.env['omniauth.params'] && request.env['omniauth.params'].any?
|
97
|
+
@_omniauth_params = request.env['omniauth.params']
|
98
|
+
elsif session['dta.omniauth.params'] && session['dta.omniauth.params'].any?
|
99
|
+
@_omniauth_params ||= session.delete('dta.omniauth.params')
|
100
|
+
@_omniauth_params
|
101
|
+
elsif params['omniauth_window_type']
|
102
|
+
@_omniauth_params = params.slice('omniauth_window_type', 'auth_origin_url', 'resource_class', 'origin')
|
103
|
+
else
|
104
|
+
@_omniauth_params = {}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@_omniauth_params
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
# break out provider attribute assignment for easy method extension
|
112
|
+
def assign_provider_attrs(user, auth_hash)
|
113
|
+
attrs = auth_hash['info'].slice(*user.attribute_names)
|
114
|
+
user.assign_attributes(attrs)
|
115
|
+
end
|
116
|
+
|
117
|
+
# derive allowed params from the standard devise parameter sanitizer
|
118
|
+
def whitelisted_params
|
119
|
+
whitelist = params_for_resource(:sign_up)
|
120
|
+
|
121
|
+
whitelist.inject({}) do |coll, key|
|
122
|
+
param = omniauth_params[key.to_s]
|
123
|
+
coll[key] = param if param
|
124
|
+
coll
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def resource_class(mapping = nil)
|
129
|
+
if omniauth_params['resource_class']
|
130
|
+
omniauth_params['resource_class'].constantize
|
131
|
+
elsif params['resource_class']
|
132
|
+
params['resource_class'].constantize
|
133
|
+
else
|
134
|
+
raise 'No resource_class found'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def resource_name
|
139
|
+
resource_class
|
140
|
+
end
|
141
|
+
|
142
|
+
def omniauth_window_type
|
143
|
+
omniauth_params['omniauth_window_type']
|
144
|
+
end
|
145
|
+
|
146
|
+
def unsafe_auth_origin_url
|
147
|
+
omniauth_params['auth_origin_url'] || omniauth_params['origin']
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def auth_origin_url
|
152
|
+
if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
|
153
|
+
return nil
|
154
|
+
end
|
155
|
+
return unsafe_auth_origin_url
|
156
|
+
end
|
157
|
+
|
158
|
+
# in the success case, omniauth_window_type is in the omniauth_params.
|
159
|
+
# in the failure case, it is in a query param. See monkey patch above
|
160
|
+
def omniauth_window_type
|
161
|
+
omniauth_params.nil? ? params['omniauth_window_type'] : omniauth_params['omniauth_window_type']
|
162
|
+
end
|
163
|
+
|
164
|
+
# this sesison value is set by the redirect_callbacks method. its purpose
|
165
|
+
# is to persist the omniauth auth hash value thru a redirect. the value
|
166
|
+
# must be destroyed immediatly after it is accessed by omniauth_success
|
167
|
+
def auth_hash
|
168
|
+
@_auth_hash ||= session.delete('dta.omniauth.auth')
|
169
|
+
@_auth_hash
|
170
|
+
end
|
171
|
+
|
172
|
+
# ensure that this controller responds to :devise_controller? conditionals.
|
173
|
+
# this is used primarily for access to the parameter sanitizers.
|
174
|
+
def assert_is_devise_resource!
|
175
|
+
true
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_random_password
|
179
|
+
# set crazy password for new oauth users. this is only used to prevent
|
180
|
+
# access via email sign-in.
|
181
|
+
p = SecureRandom.urlsafe_base64(nil, false)
|
182
|
+
@resource.password = p
|
183
|
+
@resource.password_confirmation = p
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_auth_params
|
187
|
+
@auth_params = {
|
188
|
+
auth_token: @token.token,
|
189
|
+
client_id: @token.client,
|
190
|
+
uid: @resource.uid,
|
191
|
+
expiry: @token.expiry,
|
192
|
+
config: @config
|
193
|
+
}
|
194
|
+
@auth_params.merge!(oauth_registration: true) if @oauth_registration
|
195
|
+
@auth_params
|
196
|
+
end
|
197
|
+
|
198
|
+
def set_token_on_resource
|
199
|
+
@config = omniauth_params['config_name']
|
200
|
+
@token = @resource.create_token
|
201
|
+
end
|
202
|
+
|
203
|
+
def render_error_not_allowed_auth_origin_url
|
204
|
+
message = I18n.t('devise_tokens.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
|
205
|
+
render_data_or_redirect('authFailure', error: message)
|
206
|
+
end
|
207
|
+
|
208
|
+
def render_data(message, data)
|
209
|
+
@data = data.merge(message: ActionController::Base.helpers.sanitize(message))
|
210
|
+
render layout: nil, template: 'devise_tokens/omniauth_external_window'
|
211
|
+
end
|
212
|
+
|
213
|
+
def render_data_or_redirect(message, data, user_data = {})
|
214
|
+
|
215
|
+
# We handle inAppBrowser and newWindow the same, but it is nice
|
216
|
+
# to support values in case people need custom implementations for each case
|
217
|
+
# (For example, nbrustein does not allow new users to be created if logging in with
|
218
|
+
# an inAppBrowser)
|
219
|
+
#
|
220
|
+
# See app/views/devise_tokens/omniauth_external_window.html.erb to understand
|
221
|
+
# why we can handle these both the same. The view is setup to handle both cases
|
222
|
+
# at the same time.
|
223
|
+
if ['inAppBrowser', 'newWindow'].include?(omniauth_window_type)
|
224
|
+
render_data(message, user_data.merge(data))
|
225
|
+
|
226
|
+
elsif auth_origin_url # default to same-window implementation, which forwards back to auth_origin_url
|
227
|
+
|
228
|
+
# build and redirect to destination url
|
229
|
+
redirect_to DeviseTokens::Url.generate(auth_origin_url, data.merge(blank: true))
|
230
|
+
else
|
231
|
+
|
232
|
+
# there SHOULD always be an auth_origin_url, but if someone does something silly
|
233
|
+
# like coming straight to this url or refreshing the page at the wrong time, there may not be one.
|
234
|
+
# In that case, just render in plain text the error message if there is one or otherwise
|
235
|
+
# a generic message.
|
236
|
+
fallback_render data[:error] || 'An error occurred'
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def fallback_render(text)
|
241
|
+
render inline: %Q(
|
242
|
+
|
243
|
+
<html>
|
244
|
+
<head></head>
|
245
|
+
<body>
|
246
|
+
#{ActionController::Base.helpers.sanitize(text)}
|
247
|
+
</body>
|
248
|
+
</html>)
|
249
|
+
end
|
250
|
+
|
251
|
+
def handle_new_resource
|
252
|
+
@oauth_registration = true
|
253
|
+
set_random_password
|
254
|
+
end
|
255
|
+
|
256
|
+
def assign_whitelisted_params?
|
257
|
+
true
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_resource_from_auth_hash
|
261
|
+
# find or create user by provider and provider uid
|
262
|
+
@resource = resource_class.where(
|
263
|
+
uid: auth_hash['uid'],
|
264
|
+
provider: auth_hash['provider']
|
265
|
+
).first_or_initialize
|
266
|
+
|
267
|
+
if @resource.new_record?
|
268
|
+
handle_new_resource
|
269
|
+
end
|
270
|
+
|
271
|
+
# sync user info with provider, update/generate auth token
|
272
|
+
assign_provider_attrs(@resource, auth_hash)
|
273
|
+
|
274
|
+
# assign any additional (whitelisted) attributes
|
275
|
+
if assign_whitelisted_params?
|
276
|
+
extra_params = whitelisted_params
|
277
|
+
@resource.assign_attributes(extra_params) if extra_params
|
278
|
+
end
|
279
|
+
|
280
|
+
@resource
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|