devise-tokens 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/devise_tokens/application_controller.rb +77 -0
  3. data/app/controllers/devise_tokens/concerns/resource_finder.rb +42 -0
  4. data/app/controllers/devise_tokens/concerns/set_user_by_token.rb +160 -0
  5. data/app/controllers/devise_tokens/confirmations_controller.rb +79 -0
  6. data/app/controllers/devise_tokens/omniauth_callbacks_controller.rb +284 -0
  7. data/app/controllers/devise_tokens/passwords_controller.rb +204 -0
  8. data/app/controllers/devise_tokens/registrations_controller.rb +203 -0
  9. data/app/controllers/devise_tokens/sessions_controller.rb +128 -0
  10. data/app/controllers/devise_tokens/token_validations_controller.rb +29 -0
  11. data/app/controllers/devise_tokens/unlocks_controller.rb +87 -0
  12. data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
  13. data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
  14. data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
  15. data/app/models/devise_token_auth/concerns/user.rb +253 -0
  16. data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +28 -0
  17. data/app/validators/devise_token_auth_email_validator.rb +23 -0
  18. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  19. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  20. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  21. data/app/views/devise_token_auth/omniauth_external_window.html.erb +38 -0
  22. data/config/locales/da-DK.yml +52 -0
  23. data/config/locales/de.yml +51 -0
  24. data/config/locales/en.yml +57 -0
  25. data/config/locales/es.yml +51 -0
  26. data/config/locales/fr.yml +51 -0
  27. data/config/locales/he.yml +52 -0
  28. data/config/locales/it.yml +48 -0
  29. data/config/locales/ja.yml +48 -0
  30. data/config/locales/nl.yml +32 -0
  31. data/config/locales/pl.yml +50 -0
  32. data/config/locales/pt-BR.yml +48 -0
  33. data/config/locales/pt.yml +50 -0
  34. data/config/locales/ro.yml +48 -0
  35. data/config/locales/ru.yml +52 -0
  36. data/config/locales/sq.yml +48 -0
  37. data/config/locales/sv.yml +52 -0
  38. data/config/locales/uk.yml +61 -0
  39. data/config/locales/vi.yml +52 -0
  40. data/config/locales/zh-CN.yml +48 -0
  41. data/config/locales/zh-HK.yml +50 -0
  42. data/config/locales/zh-TW.yml +50 -0
  43. data/lib/devise_tokens.rb +14 -0
  44. data/lib/devise_tokens/blacklist.rb +2 -0
  45. data/lib/devise_tokens/controllers/helpers.rb +161 -0
  46. data/lib/devise_tokens/controllers/url_helpers.rb +10 -0
  47. data/lib/devise_tokens/engine.rb +92 -0
  48. data/lib/devise_tokens/errors.rb +6 -0
  49. data/lib/devise_tokens/rails/routes.rb +116 -0
  50. data/lib/devise_tokens/token_factory.rb +126 -0
  51. data/lib/devise_tokens/url.rb +39 -0
  52. data/lib/devise_tokens/version.rb +3 -0
  53. data/lib/generators/devise_tokens/USAGE +31 -0
  54. data/lib/generators/devise_tokens/install_generator.rb +91 -0
  55. data/lib/generators/devise_tokens/install_generator_helpers.rb +98 -0
  56. data/lib/generators/devise_tokens/install_mongoid_generator.rb +46 -0
  57. data/lib/generators/devise_tokens/install_views_generator.rb +18 -0
  58. data/lib/generators/devise_tokens/templates/devise_tokens.rb +55 -0
  59. data/lib/generators/devise_tokens/templates/devise_tokens_create_users.rb.erb +49 -0
  60. data/lib/generators/devise_tokens/templates/user.rb.erb +9 -0
  61. data/lib/generators/devise_tokens/templates/user_mongoid.rb.erb +56 -0
  62. data/lib/tasks/devise_tokens_tasks.rake +6 -0
  63. metadata +208 -4
  64. data/lib/devise-tokens.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78cd95c506477bf946903658700208508bf7eba1fde5bfba02b6068281550e34
4
- data.tar.gz: bffd925f0488dc99af39d2c892031a7f4b7878f1cbf57fb96a0f4b05be404571
3
+ metadata.gz: 7dafc9b9c1d7eb65cf8162acc3a06dacbd5151dfbea76da7c801564538bd275f
4
+ data.tar.gz: b798b1450394f2d804bd59efdd273fce5dad770eecf73d017248329314063d41
5
5
  SHA512:
6
- metadata.gz: f4266ac42be750d396ce6c3ea1878ea269de258326fde4af3fc298ed836f8c9c19070fd6330985268ed996624afa574bff3368d3dffb51956d70d8d746961dfb
7
- data.tar.gz: 5f779487e2966857b9e9b4d637699f8bf1b8497247e4eccae02e0cba9c6eb8a7a60c0b03129e6509e0bb805ace2548f1121403b892f2f070d95fecbaa6729e4a
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