devise_token_auth 1.0.0 → 1.2.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 +5 -5
- data/README.md +4 -2
- data/app/controllers/devise_token_auth/application_controller.rb +19 -3
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +23 -11
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +78 -57
- data/app/controllers/devise_token_auth/confirmations_controller.rb +67 -20
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +82 -30
- data/app/controllers/devise_token_auth/passwords_controller.rb +53 -31
- data/app/controllers/devise_token_auth/registrations_controller.rb +33 -40
- data/app/controllers/devise_token_auth/sessions_controller.rb +24 -6
- data/app/controllers/devise_token_auth/unlocks_controller.rb +10 -6
- data/app/models/devise_token_auth/concerns/active_record_support.rb +14 -0
- data/app/models/devise_token_auth/concerns/confirmable_support.rb +28 -0
- data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
- data/app/models/devise_token_auth/concerns/tokens_serialization.rb +31 -0
- data/app/models/devise_token_auth/concerns/user.rb +77 -80
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +12 -5
- data/app/validators/{email_validator.rb → devise_token_auth_email_validator.rb} +11 -3
- data/app/views/devise_token_auth/omniauth_external_window.html.erb +1 -1
- data/config/locales/da-DK.yml +2 -0
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +10 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/he.yml +52 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/ja.yml +16 -2
- data/config/locales/ko.yml +51 -0
- data/config/locales/nl.yml +2 -0
- data/config/locales/pl.yml +6 -3
- data/config/locales/pt-BR.yml +2 -0
- data/config/locales/pt.yml +6 -3
- data/config/locales/ro.yml +2 -0
- data/config/locales/ru.yml +2 -0
- data/config/locales/sq.yml +2 -0
- data/config/locales/sv.yml +2 -0
- data/config/locales/uk.yml +2 -0
- data/config/locales/vi.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/config/locales/zh-HK.yml +2 -0
- data/config/locales/zh-TW.yml +2 -0
- data/lib/devise_token_auth/blacklist.rb +6 -0
- data/lib/devise_token_auth/controllers/helpers.rb +5 -9
- data/lib/devise_token_auth/engine.rb +17 -2
- data/lib/devise_token_auth/rails/routes.rb +18 -13
- data/lib/devise_token_auth/token_factory.rb +126 -0
- data/lib/devise_token_auth/url.rb +3 -0
- data/lib/devise_token_auth/version.rb +1 -1
- data/lib/devise_token_auth.rb +6 -3
- data/lib/generators/devise_token_auth/USAGE +1 -1
- data/lib/generators/devise_token_auth/install_generator.rb +7 -91
- data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
- data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +13 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +1 -8
- data/lib/generators/devise_token_auth/templates/user.rb.erb +2 -2
- data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
- data/test/controllers/custom/custom_confirmations_controller_test.rb +1 -1
- data/test/controllers/demo_mang_controller_test.rb +37 -8
- data/test/controllers/demo_user_controller_test.rb +39 -10
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +163 -18
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +110 -43
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +299 -122
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +54 -14
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +31 -40
- data/test/controllers/devise_token_auth/token_validations_controller_test.rb +43 -2
- data/test/controllers/devise_token_auth/unlocks_controller_test.rb +44 -5
- data/test/controllers/overrides/confirmations_controller_test.rb +1 -1
- data/test/dummy/app/active_record/confirmable_user.rb +11 -0
- data/test/dummy/app/{models → active_record}/scoped_user.rb +2 -2
- data/test/dummy/app/{models → active_record}/unconfirmable_user.rb +1 -2
- data/test/dummy/app/{models → active_record}/unregisterable_user.rb +3 -3
- data/test/dummy/app/active_record/user.rb +6 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +3 -3
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +3 -3
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +1 -1
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +2 -2
- data/test/dummy/app/models/{user.rb → concerns/favorite_color.rb} +7 -8
- data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
- data/test/dummy/app/mongoid/lockable_user.rb +38 -0
- data/test/dummy/app/mongoid/mang.rb +46 -0
- data/test/dummy/app/mongoid/only_email_user.rb +33 -0
- data/test/dummy/app/mongoid/scoped_user.rb +50 -0
- data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
- data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
- data/test/dummy/app/mongoid/user.rb +49 -0
- data/test/dummy/app/views/layouts/application.html.erb +0 -2
- data/test/dummy/config/application.rb +22 -1
- data/test/dummy/config/boot.rb +4 -0
- data/test/dummy/config/environments/development.rb +0 -10
- data/test/dummy/config/environments/production.rb +0 -16
- data/test/dummy/config/initializers/devise.rb +285 -0
- data/test/dummy/config/initializers/devise_token_auth.rb +35 -4
- data/test/dummy/config/initializers/figaro.rb +1 -1
- data/test/dummy/config/initializers/omniauth.rb +1 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +0 -7
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +0 -7
- data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +0 -7
- data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +0 -7
- data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +0 -7
- data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +0 -7
- data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +0 -7
- data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
- data/test/dummy/db/schema.rb +31 -33
- data/test/dummy/tmp/generators/app/models/user.rb +11 -0
- data/test/dummy/tmp/generators/config/initializers/devise_token_auth.rb +60 -0
- data/test/dummy/tmp/generators/db/migrate/20220822003050_devise_token_auth_create_users.rb +49 -0
- data/test/factories/users.rb +3 -2
- data/test/lib/devise_token_auth/blacklist_test.rb +19 -0
- data/test/lib/devise_token_auth/rails/custom_routes_test.rb +29 -0
- data/test/lib/devise_token_auth/rails/routes_test.rb +87 -0
- data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
- data/test/lib/devise_token_auth/url_test.rb +2 -2
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +51 -31
- data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +51 -31
- data/test/models/concerns/mongoid_support_test.rb +31 -0
- data/test/models/concerns/tokens_serialization_test.rb +104 -0
- data/test/models/confirmable_user_test.rb +35 -0
- data/test/models/only_email_user_test.rb +0 -8
- data/test/models/user_test.rb +13 -23
- data/test/test_helper.rb +45 -4
- metadata +126 -33
- data/config/initializers/devise.rb +0 -198
- data/test/dummy/config/initializers/assets.rb +0 -10
- data/test/dummy/tmp/generators/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
- data/test/dummy/tmp/generators/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
- /data/test/dummy/app/{models → active_record}/lockable_user.rb +0 -0
- /data/test/dummy/app/{models → active_record}/mang.rb +0 -0
- /data/test/dummy/app/{models → active_record}/only_email_user.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: be08ae7f01121ebe8c6b9b8fe04bcc2bdc83a2c8108452ffc986f1865278e85f
|
4
|
+
data.tar.gz: 272f45dc6f28fba16b6a523f47cbb9ecf3be9c05d4aa644ee0d0998fa5272f43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc54c90eee4fdf43e6d9b72ca905fc58e4338f1310b289bbede651978bd4f407556392d3d4c61cfaeabf6d4fba179768e02ec9c00bc245b33ba629972676c676
|
7
|
+
data.tar.gz: 00e139ae99fe395580ef8f846cca46516792b68cda0e0201e551e90ca9c70679fcf9519d3682ea82095588e78a93bb2def3db2bebe670a0e96f933e7087fee4b
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ Also, it maintains a session for each client/device, so you can have as many ses
|
|
19
19
|
|
20
20
|
* Seamless integration with:
|
21
21
|
* [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) for [AngularJS](https://github.com/angular/angular.js)
|
22
|
-
* [
|
22
|
+
* [Angular-Token](https://github.com/neroniaky/angular-token) for [Angular](https://github.com/angular/angular)
|
23
23
|
* [redux-token-auth](https://github.com/kylecorbelli/redux-token-auth) for [React with Redux](https://github.com/reactjs/react-redux)
|
24
24
|
* [jToker](https://github.com/lynndylanhurley/j-toker) for [jQuery](https://jquery.com/)
|
25
25
|
* Oauth2 authentication using [OmniAuth](https://github.com/intridea/omniauth).
|
@@ -65,11 +65,13 @@ Please read the [issue template](https://github.com/lynndylanhurley/devise_token
|
|
65
65
|
|
66
66
|
See our [Contribution Guidelines](https://github.com/lynndylanhurley/devise_token_auth/blob/master/.github/CONTRIBUTING.md). Feel free to submit pull requests, review pull requests, or review open issues. If you'd like to get in contact, [Zach Feldman](https://github.com/zachfeldman) has been wrangling this effort, you can reach him with his name @gmail. Further discussion of this in [this issue](https://github.com/lynndylanhurley/devise_token_auth/issues/969).
|
67
67
|
|
68
|
+
We have some bounties for some issues, [check them out](https://github.com/lynndylanhurley/devise_token_auth/issues?q=is%3Aopen+is%3Aissue+label%3Abounty)!
|
69
|
+
|
68
70
|
## Live Demos
|
69
71
|
|
70
72
|
[Here is a demo](http://ng-token-auth-demo.herokuapp.com/) of this app running with the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module and [AngularJS](https://github.com/angular/angular.js).
|
71
73
|
|
72
|
-
[Here is a demo](https://
|
74
|
+
[Here is a demo](https://stackblitz.com/github/neroniaky/angular-token) of this app running with the [Angular-Token](https://github.com/neroniaky/angular-token) service and [Angular](https://github.com/angular/angular).
|
73
75
|
|
74
76
|
[Here is a demo](https://j-toker-demo.herokuapp.com/) of this app using the [jToker](https://github.com/lynndylanhurley/j-toker) plugin and [React](http://facebook.github.io/react/).
|
75
77
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module DeviseTokenAuth
|
4
4
|
class ApplicationController < DeviseController
|
5
5
|
include DeviseTokenAuth::Concerns::SetUserByToken
|
6
|
-
include DeviseTokenAuth::Concerns::ResourceFinder
|
7
6
|
|
8
7
|
def resource_data(opts = {})
|
9
8
|
response_data = opts[:resource_json] || @resource.as_json
|
@@ -17,8 +16,8 @@ module DeviseTokenAuth
|
|
17
16
|
|
18
17
|
protected
|
19
18
|
|
20
|
-
def blacklisted_redirect_url?
|
21
|
-
DeviseTokenAuth.redirect_whitelist && !DeviseTokenAuth::Url.whitelisted?(
|
19
|
+
def blacklisted_redirect_url?(redirect_url)
|
20
|
+
DeviseTokenAuth.redirect_whitelist && !DeviseTokenAuth::Url.whitelisted?(redirect_url)
|
22
21
|
end
|
23
22
|
|
24
23
|
def build_redirect_headers(access_token, client, redirect_header_options = {})
|
@@ -76,5 +75,22 @@ module DeviseTokenAuth
|
|
76
75
|
response = response.merge(data) if data
|
77
76
|
render json: response, status: status
|
78
77
|
end
|
78
|
+
|
79
|
+
def success_message(name, email)
|
80
|
+
if Devise.paranoid
|
81
|
+
I18n.t("devise_token_auth.#{name}.sended_paranoid")
|
82
|
+
else
|
83
|
+
I18n.t("devise_token_auth.#{name}.sended", email: email)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# When using a cookie to transport the auth token we can set it immediately in flows such as
|
88
|
+
# reset password and OmniAuth success, rather than making the client scrape the token from
|
89
|
+
# query params (to then send in the initial validate_token request).
|
90
|
+
# TODO: We should be able to stop exposing the token in query params when this method is used
|
91
|
+
def set_token_in_cookie(resource, token)
|
92
|
+
auth_header = resource.build_auth_header(token.token, token.client)
|
93
|
+
cookies[DeviseTokenAuth.cookie_name] = DeviseTokenAuth.cookie_attributes.merge(value: auth_header.to_json)
|
94
|
+
end
|
79
95
|
end
|
80
96
|
end
|
@@ -20,21 +20,33 @@ module DeviseTokenAuth::Concerns::ResourceFinder
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def find_resource(field, value)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
@resource = if database_adapter&.include?('mysql')
|
24
|
+
# fix for mysql default case insensitivity
|
25
|
+
resource_class.where("BINARY #{field} = ? AND provider= ?", value, provider).first
|
26
|
+
else
|
27
|
+
resource_class.dta_find_by(field => value, 'provider' => provider)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def database_adapter
|
32
|
+
@database_adapter ||= begin
|
33
|
+
rails_version = [Rails::VERSION::MAJOR, Rails::VERSION::MINOR].join(".")
|
28
34
|
|
29
|
-
|
35
|
+
adapter =
|
36
|
+
if rails_version >= "6.1"
|
37
|
+
resource_class.try(:connection_db_config)&.try(:adapter)
|
38
|
+
else
|
39
|
+
resource_class.try(:connection_config)&.try(:[], :adapter)
|
40
|
+
end
|
41
|
+
end
|
30
42
|
end
|
31
43
|
|
32
44
|
def resource_class(m = nil)
|
33
|
-
if m
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
mapping = if m
|
46
|
+
Devise.mappings[m]
|
47
|
+
else
|
48
|
+
Devise.mappings[resource_name] || Devise.mappings.values.first
|
49
|
+
end
|
38
50
|
|
39
51
|
mapping.to
|
40
52
|
end
|
@@ -17,24 +17,11 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
17
17
|
@used_auth_by_token = true
|
18
18
|
|
19
19
|
# initialize instance variables
|
20
|
-
@
|
20
|
+
@token ||= DeviseTokenAuth::TokenFactory.new
|
21
21
|
@resource ||= nil
|
22
|
-
@token ||= nil
|
23
22
|
@is_batch_request ||= nil
|
24
23
|
end
|
25
24
|
|
26
|
-
def ensure_pristine_resource
|
27
|
-
if @resource.changed?
|
28
|
-
# Stash pending changes in the resource before reloading.
|
29
|
-
changes = @resource.changes
|
30
|
-
@resource.reload
|
31
|
-
end
|
32
|
-
yield
|
33
|
-
ensure
|
34
|
-
# Reapply pending changes
|
35
|
-
@resource.assign_attributes(changes) if changes
|
36
|
-
end
|
37
|
-
|
38
25
|
# user auth
|
39
26
|
def set_user_by_token(mapping = nil)
|
40
27
|
# determine target authentication class
|
@@ -45,21 +32,37 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
45
32
|
|
46
33
|
# gets the headers names, which was set in the initialize file
|
47
34
|
uid_name = DeviseTokenAuth.headers_names[:'uid']
|
35
|
+
other_uid_name = DeviseTokenAuth.other_uid && DeviseTokenAuth.headers_names[DeviseTokenAuth.other_uid.to_sym]
|
48
36
|
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
|
49
37
|
client_name = DeviseTokenAuth.headers_names[:'client']
|
38
|
+
authorization_name = DeviseTokenAuth.headers_names[:"authorization"]
|
39
|
+
|
40
|
+
# Read Authorization token and decode it if present
|
41
|
+
decoded_authorization_token = decode_bearer_token(request.headers[authorization_name])
|
42
|
+
|
43
|
+
# gets values from cookie if configured and present
|
44
|
+
parsed_auth_cookie = {}
|
45
|
+
if DeviseTokenAuth.cookie_enabled
|
46
|
+
auth_cookie = request.cookies[DeviseTokenAuth.cookie_name]
|
47
|
+
if auth_cookie.present?
|
48
|
+
parsed_auth_cookie = JSON.parse(auth_cookie)
|
49
|
+
end
|
50
|
+
end
|
50
51
|
|
51
52
|
# parse header for values necessary for authentication
|
52
|
-
uid
|
53
|
-
|
54
|
-
@
|
53
|
+
uid = request.headers[uid_name] || params[uid_name] || parsed_auth_cookie[uid_name] || decoded_authorization_token[uid_name]
|
54
|
+
other_uid = other_uid_name && request.headers[other_uid_name] || params[other_uid_name] || parsed_auth_cookie[other_uid_name]
|
55
|
+
@token = DeviseTokenAuth::TokenFactory.new unless @token
|
56
|
+
@token.token ||= request.headers[access_token_name] || params[access_token_name] || parsed_auth_cookie[access_token_name] || decoded_authorization_token[access_token_name]
|
57
|
+
@token.client ||= request.headers[client_name] || params[client_name] || parsed_auth_cookie[client_name] || decoded_authorization_token[client_name]
|
55
58
|
|
56
|
-
#
|
57
|
-
@
|
59
|
+
# client isn't required, set to 'default' if absent
|
60
|
+
@token.client ||= 'default'
|
58
61
|
|
59
62
|
# check for an existing user, authenticated via warden/devise, if enabled
|
60
63
|
if DeviseTokenAuth.enable_standard_devise_support
|
61
|
-
devise_warden_user = warden.user(
|
62
|
-
if devise_warden_user && devise_warden_user.tokens[@
|
64
|
+
devise_warden_user = warden.user(mapping)
|
65
|
+
if devise_warden_user && devise_warden_user.tokens[@token.client].nil?
|
63
66
|
@used_auth_by_token = false
|
64
67
|
@resource = devise_warden_user
|
65
68
|
# REVIEW: The following line _should_ be safe to remove;
|
@@ -71,53 +74,55 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
71
74
|
# user has already been found and authenticated
|
72
75
|
return @resource if @resource && @resource.is_a?(rc)
|
73
76
|
|
74
|
-
# ensure we clear the
|
75
|
-
unless @token
|
76
|
-
@
|
77
|
+
# ensure we clear the client
|
78
|
+
unless @token.present?
|
79
|
+
@token.client = nil
|
77
80
|
return
|
78
81
|
end
|
79
82
|
|
80
|
-
return false unless @token
|
81
|
-
|
82
83
|
# mitigate timing attacks by finding by uid instead of auth token
|
83
|
-
user = uid && rc.
|
84
|
+
user = (uid && rc.dta_find_by(uid: uid)) || (other_uid && rc.dta_find_by("#{DeviseTokenAuth.other_uid}": other_uid))
|
85
|
+
scope = rc.to_s.underscore.to_sym
|
84
86
|
|
85
|
-
if user && user.valid_token?(@token, @
|
87
|
+
if user && user.valid_token?(@token.token, @token.client)
|
86
88
|
# sign_in with bypass: true will be deprecated in the next version of Devise
|
87
89
|
if respond_to?(:bypass_sign_in) && DeviseTokenAuth.bypass_sign_in
|
88
|
-
bypass_sign_in(user, scope:
|
90
|
+
bypass_sign_in(user, scope: scope)
|
89
91
|
else
|
90
|
-
sign_in(
|
92
|
+
sign_in(scope, user, store: false, event: :fetch, bypass: DeviseTokenAuth.bypass_sign_in)
|
91
93
|
end
|
92
94
|
return @resource = user
|
93
95
|
else
|
94
96
|
# zero all values previously set values
|
95
|
-
@
|
97
|
+
@token.client = nil
|
96
98
|
return @resource = nil
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
100
102
|
def update_auth_header
|
101
103
|
# cannot save object if model has invalid params
|
104
|
+
return unless @resource && @token.client
|
102
105
|
|
103
|
-
|
104
|
-
|
105
|
-
# Generate new client_id with existing authentication
|
106
|
-
@client_id = nil unless @used_auth_by_token
|
106
|
+
# Generate new client with existing authentication
|
107
|
+
@token.client = nil unless @used_auth_by_token
|
107
108
|
|
108
109
|
if @used_auth_by_token && !DeviseTokenAuth.change_headers_on_each_request
|
109
110
|
# should not append auth header if @resource related token was
|
110
111
|
# cleared by sign out in the meantime
|
111
|
-
return if @resource.reload.tokens[@
|
112
|
+
return if @resource.reload.tokens[@token.client].nil?
|
112
113
|
|
113
|
-
auth_header = @resource.build_auth_header(@token, @
|
114
|
+
auth_header = @resource.build_auth_header(@token.token, @token.client)
|
114
115
|
|
115
116
|
# update the response header
|
116
117
|
response.headers.merge!(auth_header)
|
117
118
|
|
119
|
+
# set a server cookie if configured
|
120
|
+
if DeviseTokenAuth.cookie_enabled
|
121
|
+
set_cookie(auth_header)
|
122
|
+
end
|
118
123
|
else
|
119
124
|
unless @resource.reload.valid?
|
120
|
-
@resource =
|
125
|
+
@resource = @resource.class.find(@resource.to_param) # errors remain after reload
|
121
126
|
# if we left the model in a bad state, something is wrong in our app
|
122
127
|
unless @resource.valid?
|
123
128
|
raise DeviseTokenAuth::Errors::InvalidModel, "Cannot set auth token in invalid model. Errors: #{@resource.errors.full_messages}"
|
@@ -129,38 +134,54 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
129
134
|
|
130
135
|
private
|
131
136
|
|
137
|
+
def decode_bearer_token(bearer_token)
|
138
|
+
return {} if bearer_token.blank?
|
139
|
+
|
140
|
+
encoded_token = bearer_token.split.last # Removes the 'Bearer' from the string
|
141
|
+
JSON.parse(Base64.strict_decode64(encoded_token)) rescue {}
|
142
|
+
end
|
143
|
+
|
132
144
|
def refresh_headers
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@resource
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
+
# Lock the user record during any auth_header updates to ensure
|
146
|
+
# we don't have write contention from multiple threads
|
147
|
+
@resource.with_lock do
|
148
|
+
# should not append auth header if @resource related token was
|
149
|
+
# cleared by sign out in the meantime
|
150
|
+
return if @used_auth_by_token && @resource.tokens[@token.client].nil?
|
151
|
+
|
152
|
+
_auth_header_from_batch_request = auth_header_from_batch_request
|
153
|
+
|
154
|
+
# update the response header
|
155
|
+
response.headers.merge!(_auth_header_from_batch_request)
|
156
|
+
|
157
|
+
# set a server cookie if configured
|
158
|
+
if DeviseTokenAuth.cookie_enabled
|
159
|
+
set_cookie(_auth_header_from_batch_request)
|
160
|
+
end
|
161
|
+
end # end lock
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_cookie(auth_header)
|
165
|
+
cookies[DeviseTokenAuth.cookie_name] = DeviseTokenAuth.cookie_attributes.merge(value: auth_header.to_json)
|
145
166
|
end
|
146
167
|
|
147
|
-
def is_batch_request?(user,
|
168
|
+
def is_batch_request?(user, client)
|
148
169
|
!params[:unbatch] &&
|
149
|
-
user.tokens[
|
150
|
-
user.tokens[
|
151
|
-
|
170
|
+
user.tokens[client] &&
|
171
|
+
user.tokens[client]['updated_at'] &&
|
172
|
+
user.tokens[client]['updated_at'].to_time > @request_started_at - DeviseTokenAuth.batch_request_buffer_throttle
|
152
173
|
end
|
153
174
|
|
154
175
|
def auth_header_from_batch_request
|
155
176
|
# determine batch request status after request processing, in case
|
156
177
|
# another processes has updated it during that processing
|
157
|
-
@is_batch_request = is_batch_request?(@resource, @
|
178
|
+
@is_batch_request = is_batch_request?(@resource, @token.client)
|
158
179
|
|
159
180
|
auth_header = {}
|
160
181
|
# extend expiration of batch buffer to account for the duration of
|
161
182
|
# this request
|
162
183
|
if @is_batch_request
|
163
|
-
auth_header = @resource.extend_batch_buffer(@token, @
|
184
|
+
auth_header = @resource.extend_batch_buffer(@token.token, @token.client)
|
164
185
|
|
165
186
|
# Do not return token for batch requests to avoid invalidated
|
166
187
|
# tokens returned to the client in case of race conditions.
|
@@ -171,7 +192,7 @@ module DeviseTokenAuth::Concerns::SetUserByToken
|
|
171
192
|
auth_header[DeviseTokenAuth.headers_names[:"expiry"]] = ' '
|
172
193
|
else
|
173
194
|
# update Authorization response header with new token
|
174
|
-
auth_header = @resource.create_new_auth_token(@
|
195
|
+
auth_header = @resource.create_new_auth_token(@token.client)
|
175
196
|
end
|
176
197
|
auth_header
|
177
198
|
end
|
@@ -2,38 +2,85 @@
|
|
2
2
|
|
3
3
|
module DeviseTokenAuth
|
4
4
|
class ConfirmationsController < DeviseTokenAuth::ApplicationController
|
5
|
-
def show
|
6
|
-
@resource = resource_class.confirm_by_token(params[:confirmation_token])
|
7
|
-
|
8
|
-
if @resource && @resource.id
|
9
|
-
expiry = nil
|
10
|
-
if defined?(@resource.sign_in_count) && @resource.sign_in_count > 0
|
11
|
-
expiry = (Time.zone.now + 1.second).to_i
|
12
|
-
end
|
13
5
|
|
14
|
-
|
15
|
-
|
16
|
-
sign_in(@resource)
|
17
|
-
@resource.save!
|
6
|
+
def show
|
7
|
+
@resource = resource_class.confirm_by_token(resource_params[:confirmation_token])
|
18
8
|
|
9
|
+
if @resource.errors.empty?
|
19
10
|
yield @resource if block_given?
|
20
11
|
|
21
12
|
redirect_header_options = { account_confirmation_success: true }
|
22
|
-
redirect_headers = build_redirect_headers(token,
|
23
|
-
client_id,
|
24
|
-
redirect_header_options)
|
25
13
|
|
26
|
-
|
27
|
-
|
14
|
+
if signed_in?(resource_name)
|
15
|
+
token = signed_in_resource.create_token
|
16
|
+
signed_in_resource.save!
|
28
17
|
|
29
|
-
|
30
|
-
|
18
|
+
redirect_headers = build_redirect_headers(token.token,
|
19
|
+
token.client,
|
20
|
+
redirect_header_options)
|
31
21
|
|
22
|
+
redirect_to_link = signed_in_resource.build_auth_url(redirect_url, redirect_headers)
|
23
|
+
else
|
24
|
+
redirect_to_link = DeviseTokenAuth::Url.generate(redirect_url, redirect_header_options)
|
25
|
+
end
|
32
26
|
|
33
|
-
redirect_to(
|
27
|
+
redirect_to(redirect_to_link)
|
34
28
|
else
|
35
29
|
raise ActionController::RoutingError, 'Not Found'
|
36
30
|
end
|
37
31
|
end
|
32
|
+
|
33
|
+
def create
|
34
|
+
return render_create_error_missing_email if resource_params[:email].blank?
|
35
|
+
|
36
|
+
@email = get_case_insensitive_field_from_resource_params(:email)
|
37
|
+
|
38
|
+
@resource = resource_class.dta_find_by(uid: @email, provider: provider)
|
39
|
+
|
40
|
+
return render_not_found_error unless @resource
|
41
|
+
|
42
|
+
@resource.send_confirmation_instructions({
|
43
|
+
redirect_url: redirect_url,
|
44
|
+
client_config: resource_params[:config_name]
|
45
|
+
})
|
46
|
+
|
47
|
+
return render_create_success
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def render_create_error_missing_email
|
53
|
+
render_error(401, I18n.t('devise_token_auth.confirmations.missing_email'))
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_create_success
|
57
|
+
render json: {
|
58
|
+
success: true,
|
59
|
+
message: success_message('confirmations', @email)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_not_found_error
|
64
|
+
if Devise.paranoid
|
65
|
+
render_create_success
|
66
|
+
else
|
67
|
+
render_error(404, I18n.t('devise_token_auth.confirmations.user_not_found', email: @email))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def resource_params
|
74
|
+
params.permit(:email, :confirmation_token, :config_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
# give redirect value from params priority or fall back to default value if provided
|
78
|
+
def redirect_url
|
79
|
+
params.fetch(
|
80
|
+
:redirect_url,
|
81
|
+
DeviseTokenAuth.default_confirm_success_url
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
38
85
|
end
|
39
86
|
end
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module DeviseTokenAuth
|
4
4
|
class OmniauthCallbacksController < DeviseTokenAuth::ApplicationController
|
5
5
|
attr_reader :auth_params
|
6
|
+
|
7
|
+
before_action :validate_auth_origin_url_param
|
8
|
+
|
6
9
|
skip_before_action :set_user_by_token, raise: false
|
7
10
|
skip_after_action :update_auth_header
|
8
11
|
|
@@ -12,18 +15,43 @@ module DeviseTokenAuth
|
|
12
15
|
|
13
16
|
# derive target redirect route from 'resource_class' param, which was set
|
14
17
|
# before authentication.
|
15
|
-
devise_mapping =
|
16
|
-
|
17
|
-
path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
|
18
|
-
klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
|
19
|
-
redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
|
18
|
+
devise_mapping = get_devise_mapping
|
19
|
+
redirect_route = get_redirect_route(devise_mapping)
|
20
20
|
|
21
21
|
# preserve omniauth info for success route. ignore 'extra' in twitter
|
22
22
|
# auth response to avoid CookieOverflow.
|
23
23
|
session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
|
24
24
|
session['dta.omniauth.params'] = request.env['omniauth.params']
|
25
25
|
|
26
|
-
redirect_to redirect_route
|
26
|
+
redirect_to redirect_route, status: 307
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_redirect_route(devise_mapping)
|
30
|
+
path = "#{Devise.mappings[devise_mapping.to_sym].fullpath}/#{params[:provider]}/callback"
|
31
|
+
klass = request.scheme == 'https' ? URI::HTTPS : URI::HTTP
|
32
|
+
redirect_route = klass.build(host: request.host, port: request.port, path: path).to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_devise_mapping
|
36
|
+
# derive target redirect route from 'resource_class' param, which was set
|
37
|
+
# before authentication.
|
38
|
+
devise_mapping = [request.env['omniauth.params']['namespace_name'],
|
39
|
+
request.env['omniauth.params']['resource_class'].underscore.gsub('/', '_')].compact.join('_')
|
40
|
+
rescue NoMethodError => err
|
41
|
+
default_devise_mapping
|
42
|
+
end
|
43
|
+
|
44
|
+
# This method will only be called if `get_devise_mapping` cannot
|
45
|
+
# find the mapping in `omniauth.params`.
|
46
|
+
#
|
47
|
+
# One example use-case here is for IDP-initiated SAML login. In that
|
48
|
+
# case, there will have been no initial request in which to save
|
49
|
+
# the devise mapping. If you are in a situation like that, and
|
50
|
+
# your app allows for you to determine somehow what the devise
|
51
|
+
# mapping should be (because, for example, it is always the same),
|
52
|
+
# then you can handle it by overriding this method.
|
53
|
+
def default_devise_mapping
|
54
|
+
raise NotImplementedError.new('no default_devise_mapping set')
|
27
55
|
end
|
28
56
|
|
29
57
|
def omniauth_success
|
@@ -42,6 +70,10 @@ module DeviseTokenAuth
|
|
42
70
|
|
43
71
|
yield @resource if block_given?
|
44
72
|
|
73
|
+
if DeviseTokenAuth.cookie_enabled
|
74
|
+
set_token_in_cookie(@resource, @token)
|
75
|
+
end
|
76
|
+
|
45
77
|
render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
|
46
78
|
end
|
47
79
|
|
@@ -50,6 +82,11 @@ module DeviseTokenAuth
|
|
50
82
|
render_data_or_redirect('authFailure', error: @error)
|
51
83
|
end
|
52
84
|
|
85
|
+
def validate_auth_origin_url_param
|
86
|
+
return render_error_not_allowed_auth_origin_url if auth_origin_url && blacklisted_redirect_url?(auth_origin_url)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
53
90
|
protected
|
54
91
|
|
55
92
|
# this will be determined differently depending on the action that calls
|
@@ -79,7 +116,8 @@ module DeviseTokenAuth
|
|
79
116
|
|
80
117
|
# break out provider attribute assignment for easy method extension
|
81
118
|
def assign_provider_attrs(user, auth_hash)
|
82
|
-
attrs = auth_hash['info'].
|
119
|
+
attrs = auth_hash['info'].to_hash
|
120
|
+
attrs = attrs.slice(*user.attribute_names)
|
83
121
|
user.assign_attributes(attrs)
|
84
122
|
end
|
85
123
|
|
@@ -112,10 +150,18 @@ module DeviseTokenAuth
|
|
112
150
|
omniauth_params['omniauth_window_type']
|
113
151
|
end
|
114
152
|
|
115
|
-
def
|
153
|
+
def unsafe_auth_origin_url
|
116
154
|
omniauth_params['auth_origin_url'] || omniauth_params['origin']
|
117
155
|
end
|
118
156
|
|
157
|
+
|
158
|
+
def auth_origin_url
|
159
|
+
if unsafe_auth_origin_url && blacklisted_redirect_url?(unsafe_auth_origin_url)
|
160
|
+
return nil
|
161
|
+
end
|
162
|
+
return unsafe_auth_origin_url
|
163
|
+
end
|
164
|
+
|
119
165
|
# in the success case, omniauth_window_type is in the omniauth_params.
|
120
166
|
# in the failure case, it is in a query param. See monkey patch above
|
121
167
|
def omniauth_window_type
|
@@ -136,16 +182,6 @@ module DeviseTokenAuth
|
|
136
182
|
true
|
137
183
|
end
|
138
184
|
|
139
|
-
# necessary for access to devise_parameter_sanitizers
|
140
|
-
def devise_mapping
|
141
|
-
if omniauth_params
|
142
|
-
Devise.mappings[[omniauth_params['namespace_name'],
|
143
|
-
omniauth_params['resource_class'].underscore].compact.join('_').to_sym]
|
144
|
-
else
|
145
|
-
request.env['devise.mapping']
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
185
|
def set_random_password
|
150
186
|
# set crazy password for new oauth users. this is only used to prevent
|
151
187
|
# access via email sign-in.
|
@@ -156,11 +192,11 @@ module DeviseTokenAuth
|
|
156
192
|
|
157
193
|
def create_auth_params
|
158
194
|
@auth_params = {
|
159
|
-
auth_token:
|
160
|
-
client_id:
|
161
|
-
uid:
|
162
|
-
expiry:
|
163
|
-
config:
|
195
|
+
auth_token: @token.token,
|
196
|
+
client_id: @token.client,
|
197
|
+
uid: @resource.uid,
|
198
|
+
expiry: @token.expiry,
|
199
|
+
config: @config
|
164
200
|
}
|
165
201
|
@auth_params.merge!(oauth_registration: true) if @oauth_registration
|
166
202
|
@auth_params
|
@@ -168,11 +204,16 @@ module DeviseTokenAuth
|
|
168
204
|
|
169
205
|
def set_token_on_resource
|
170
206
|
@config = omniauth_params['config_name']
|
171
|
-
@
|
207
|
+
@token = @resource.create_token
|
208
|
+
end
|
209
|
+
|
210
|
+
def render_error_not_allowed_auth_origin_url
|
211
|
+
message = I18n.t('devise_token_auth.omniauth.not_allowed_redirect_url', redirect_url: unsafe_auth_origin_url)
|
212
|
+
render_data_or_redirect('authFailure', error: message)
|
172
213
|
end
|
173
214
|
|
174
215
|
def render_data(message, data)
|
175
|
-
@data = data.merge(message: message)
|
216
|
+
@data = data.merge(message: ActionController::Base.helpers.sanitize(message))
|
176
217
|
render layout: nil, template: 'devise_token_auth/omniauth_external_window'
|
177
218
|
end
|
178
219
|
|
@@ -209,11 +250,20 @@ module DeviseTokenAuth
|
|
209
250
|
<html>
|
210
251
|
<head></head>
|
211
252
|
<body>
|
212
|
-
#{text}
|
253
|
+
#{ActionController::Base.helpers.sanitize(text)}
|
213
254
|
</body>
|
214
255
|
</html>)
|
215
256
|
end
|
216
257
|
|
258
|
+
def handle_new_resource
|
259
|
+
@oauth_registration = true
|
260
|
+
set_random_password
|
261
|
+
end
|
262
|
+
|
263
|
+
def assign_whitelisted_params?
|
264
|
+
true
|
265
|
+
end
|
266
|
+
|
217
267
|
def get_resource_from_auth_hash
|
218
268
|
# find or create user by provider and provider uid
|
219
269
|
@resource = resource_class.where(
|
@@ -222,18 +272,20 @@ module DeviseTokenAuth
|
|
222
272
|
).first_or_initialize
|
223
273
|
|
224
274
|
if @resource.new_record?
|
225
|
-
|
226
|
-
set_random_password
|
275
|
+
handle_new_resource
|
227
276
|
end
|
228
277
|
|
229
278
|
# sync user info with provider, update/generate auth token
|
230
279
|
assign_provider_attrs(@resource, auth_hash)
|
231
280
|
|
232
281
|
# assign any additional (whitelisted) attributes
|
233
|
-
|
234
|
-
|
282
|
+
if assign_whitelisted_params?
|
283
|
+
extra_params = whitelisted_params
|
284
|
+
@resource.assign_attributes(extra_params) if extra_params
|
285
|
+
end
|
235
286
|
|
236
287
|
@resource
|
237
288
|
end
|
238
289
|
end
|
290
|
+
|
239
291
|
end
|