sorcery 0.11.0 → 0.15.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.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +20 -0
  3. data/.rubocop.yml +55 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +3 -52
  6. data/CHANGELOG.md +69 -0
  7. data/Gemfile +3 -3
  8. data/{LICENSE.txt → LICENSE.md} +1 -1
  9. data/README.md +34 -7
  10. data/lib/generators/sorcery/USAGE +1 -1
  11. data/lib/generators/sorcery/install_generator.rb +21 -21
  12. data/lib/generators/sorcery/templates/initializer.rb +164 -69
  13. data/lib/generators/sorcery/templates/migration/activity_logging.rb +4 -4
  14. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -3
  15. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  16. data/lib/generators/sorcery/templates/migration/external.rb +3 -3
  17. data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
  18. data/lib/generators/sorcery/templates/migration/remember_me.rb +2 -2
  19. data/lib/generators/sorcery/templates/migration/reset_password.rb +4 -3
  20. data/lib/generators/sorcery/templates/migration/user_activation.rb +3 -3
  21. data/lib/sorcery.rb +2 -0
  22. data/lib/sorcery/adapters/active_record_adapter.rb +3 -2
  23. data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
  24. data/lib/sorcery/controller.rb +26 -15
  25. data/lib/sorcery/controller/config.rb +2 -0
  26. data/lib/sorcery/controller/submodules/activity_logging.rb +14 -3
  27. data/lib/sorcery/controller/submodules/brute_force_protection.rb +7 -3
  28. data/lib/sorcery/controller/submodules/external.rb +48 -33
  29. data/lib/sorcery/controller/submodules/http_basic_auth.rb +5 -1
  30. data/lib/sorcery/controller/submodules/remember_me.rb +9 -10
  31. data/lib/sorcery/controller/submodules/session_timeout.rb +32 -6
  32. data/lib/sorcery/crypto_providers/aes256.rb +2 -1
  33. data/lib/sorcery/crypto_providers/bcrypt.rb +8 -2
  34. data/lib/sorcery/engine.rb +16 -3
  35. data/lib/sorcery/model.rb +14 -10
  36. data/lib/sorcery/model/config.rb +12 -4
  37. data/lib/sorcery/model/submodules/brute_force_protection.rb +6 -7
  38. data/lib/sorcery/model/submodules/external.rb +19 -3
  39. data/lib/sorcery/model/submodules/magic_login.rb +130 -0
  40. data/lib/sorcery/model/submodules/reset_password.rb +25 -2
  41. data/lib/sorcery/model/submodules/user_activation.rb +1 -1
  42. data/lib/sorcery/model/temporary_token.rb +3 -1
  43. data/lib/sorcery/protocols/oauth.rb +1 -0
  44. data/lib/sorcery/providers/auth0.rb +46 -0
  45. data/lib/sorcery/providers/discord.rb +52 -0
  46. data/lib/sorcery/providers/heroku.rb +1 -0
  47. data/lib/sorcery/providers/instagram.rb +73 -0
  48. data/lib/sorcery/providers/line.rb +47 -0
  49. data/lib/sorcery/providers/linkedin.rb +45 -36
  50. data/lib/sorcery/providers/vk.rb +5 -4
  51. data/lib/sorcery/providers/wechat.rb +8 -6
  52. data/lib/sorcery/test_helpers/internal.rb +5 -4
  53. data/lib/sorcery/test_helpers/internal/rails.rb +11 -11
  54. data/lib/sorcery/test_helpers/rails/request.rb +20 -0
  55. data/lib/sorcery/version.rb +1 -1
  56. data/sorcery.gemspec +28 -11
  57. data/spec/active_record/user_activation_spec.rb +2 -2
  58. data/spec/active_record/user_activity_logging_spec.rb +2 -2
  59. data/spec/active_record/user_brute_force_protection_spec.rb +2 -2
  60. data/spec/active_record/user_magic_login_spec.rb +15 -0
  61. data/spec/active_record/user_oauth_spec.rb +2 -2
  62. data/spec/active_record/user_remember_me_spec.rb +2 -2
  63. data/spec/active_record/user_reset_password_spec.rb +2 -2
  64. data/spec/active_record/user_spec.rb +0 -10
  65. data/spec/controllers/controller_http_basic_auth_spec.rb +1 -1
  66. data/spec/controllers/controller_oauth2_spec.rb +212 -123
  67. data/spec/controllers/controller_oauth_spec.rb +7 -7
  68. data/spec/controllers/controller_remember_me_spec.rb +16 -8
  69. data/spec/controllers/controller_session_timeout_spec.rb +90 -3
  70. data/spec/controllers/controller_spec.rb +13 -3
  71. data/spec/orm/active_record.rb +2 -2
  72. data/spec/providers/example_provider_spec.rb +17 -0
  73. data/spec/providers/example_spec.rb +17 -0
  74. data/spec/providers/vk_spec.rb +42 -0
  75. data/spec/rails_app/app/assets/config/manifest.js +1 -0
  76. data/spec/rails_app/app/controllers/sorcery_controller.rb +131 -32
  77. data/spec/rails_app/app/mailers/sorcery_mailer.rb +7 -0
  78. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
  79. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
  80. data/spec/rails_app/config/application.rb +8 -3
  81. data/spec/rails_app/config/boot.rb +1 -1
  82. data/spec/rails_app/config/environment.rb +1 -1
  83. data/spec/rails_app/config/routes.rb +14 -0
  84. data/spec/rails_app/config/secrets.yml +4 -0
  85. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +2 -2
  86. data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
  87. data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
  88. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +2 -0
  89. data/spec/rails_app/db/schema.rb +7 -9
  90. data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
  91. data/spec/shared_examples/user_oauth_shared_examples.rb +1 -1
  92. data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
  93. data/spec/shared_examples/user_reset_password_shared_examples.rb +37 -5
  94. data/spec/shared_examples/user_shared_examples.rb +104 -43
  95. data/spec/sorcery_crypto_providers_spec.rb +61 -1
  96. data/spec/sorcery_temporary_token_spec.rb +27 -0
  97. data/spec/spec.opts +1 -1
  98. data/spec/spec_helper.rb +2 -2
  99. data/spec/support/migration_helper.rb +19 -0
  100. data/spec/support/providers/example.rb +11 -0
  101. data/spec/support/providers/example_provider.rb +11 -0
  102. metadata +89 -33
  103. data/gemfiles/active_record-rails40.gemfile +0 -7
  104. data/gemfiles/active_record-rails41.gemfile +0 -7
  105. data/gemfiles/active_record-rails42.gemfile +0 -7
  106. data/spec/rails_app/config/initializers/secret_token.rb +0 -7
@@ -16,6 +16,8 @@ module Sorcery
16
16
  attr_accessor :reset_password_token_attribute_name
17
17
  # Expires at attribute name.
18
18
  attr_accessor :reset_password_token_expires_at_attribute_name
19
+ # Counter access to reset password page
20
+ attr_accessor :reset_password_page_access_count_attribute_name
19
21
  # When was email sent, used for hammering protection.
20
22
  attr_accessor :reset_password_email_sent_at_attribute_name
21
23
  # Mailer class (needed)
@@ -34,6 +36,8 @@ module Sorcery
34
36
  base.sorcery_config.instance_eval do
35
37
  @defaults.merge!(:@reset_password_token_attribute_name => :reset_password_token,
36
38
  :@reset_password_token_expires_at_attribute_name => :reset_password_token_expires_at,
39
+ :@reset_password_page_access_count_attribute_name =>
40
+ :access_count_to_reset_password_page,
37
41
  :@reset_password_email_sent_at_attribute_name => :reset_password_email_sent_at,
38
42
  :@reset_password_mailer => nil,
39
43
  :@reset_password_mailer_disabled => false,
@@ -97,6 +101,7 @@ module Sorcery
97
101
  config = sorcery_config
98
102
  # hammering protection
99
103
  return false if config.reset_password_time_between_emails.present? && send(config.reset_password_email_sent_at_attribute_name) && send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.seconds.ago.utc
104
+
100
105
  self.class.sorcery_adapter.transaction do
101
106
  generate_reset_password_token!
102
107
  mail = send_reset_password_email! unless config.reset_password_mailer_disabled
@@ -104,11 +109,29 @@ module Sorcery
104
109
  mail
105
110
  end
106
111
 
112
+ # Increment access_count_to_reset_password_page attribute.
113
+ # For example, access_count_to_reset_password_page attribute is over 1, which
114
+ # means the user doesn't have a right to access.
115
+ def increment_password_reset_page_access_counter
116
+ sorcery_adapter.increment(sorcery_config.reset_password_page_access_count_attribute_name)
117
+ end
118
+
119
+ # Reset access_count_to_reset_password_page attribute into 0.
120
+ # This is expected to be used after sending an instruction email.
121
+ def reset_password_reset_page_access_counter
122
+ send(:"#{sorcery_config.reset_password_page_access_count_attribute_name}=", 0)
123
+ sorcery_adapter.save
124
+ end
125
+
107
126
  # Clears token and tries to update the new password for the user.
108
- def change_password!(new_password)
127
+ def change_password(new_password, raise_on_failure: false)
109
128
  clear_reset_password_token
110
129
  send(:"#{sorcery_config.password_attribute_name}=", new_password)
111
- sorcery_adapter.save
130
+ sorcery_adapter.save raise_on_failure: raise_on_failure
131
+ end
132
+
133
+ def change_password!(new_password)
134
+ change_password(new_password, raise_on_failure: true)
112
135
  end
113
136
 
114
137
  protected
@@ -45,7 +45,7 @@ module Sorcery
45
45
  # don't setup activation if no password supplied - this user is created automatically
46
46
  sorcery_adapter.define_callback :before, :create, :setup_activation, if: proc { |user| user.send(sorcery_config.password_attribute_name).present? }
47
47
  # don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
48
- sorcery_adapter.define_callback :after, :create, :send_activation_needed_email!, if: :send_activation_needed_email?
48
+ sorcery_adapter.define_callback :after, :commit, :send_activation_needed_email!, on: :create, if: :send_activation_needed_email?
49
49
  end
50
50
 
51
51
  base.sorcery_config.after_config << :validate_mailer_defined
@@ -7,12 +7,14 @@ module Sorcery
7
7
  # such as reseting password and activating the user by email.
8
8
  module TemporaryToken
9
9
  def self.included(base)
10
+ # FIXME: This may not be the ideal way of passing sorcery_config to generate_random_token.
11
+ @sorcery_config = base.sorcery_config
10
12
  base.extend(ClassMethods)
11
13
  end
12
14
 
13
15
  # Random code, used for salt and temporary tokens.
14
16
  def self.generate_random_token
15
- SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz')
17
+ SecureRandom.urlsafe_base64(@sorcery_config.token_randomness).tr('lIO0', 'sxyz')
16
18
  end
17
19
 
18
20
  module ClassMethods
@@ -9,6 +9,7 @@ module Sorcery
9
9
 
10
10
  def get_request_token(token = nil, secret = nil)
11
11
  return ::OAuth::RequestToken.new(get_consumer, token, secret) if token && secret
12
+
12
13
  get_consumer.get_request_token(oauth_callback: @callback_url)
13
14
  end
14
15
 
@@ -0,0 +1,46 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with Auth0.com
4
+ #
5
+ # config.auth0.key = <key>
6
+ # config.auth0.secret = <secret>
7
+ # config.auth0.domain = <domain>
8
+ # ...
9
+ #
10
+ class Auth0 < Base
11
+ include Protocols::Oauth2
12
+
13
+ attr_accessor :auth_path, :token_path, :user_info_path, :scope
14
+
15
+ def initialize
16
+ super
17
+
18
+ @auth_path = '/authorize'
19
+ @token_path = '/oauth/token'
20
+ @user_info_path = '/userinfo'
21
+ @scope = 'openid profile email'
22
+ end
23
+
24
+ def get_user_hash(access_token)
25
+ response = access_token.get(user_info_path)
26
+
27
+ auth_hash(access_token).tap do |h|
28
+ h[:user_info] = JSON.parse(response.body)
29
+ h[:uid] = h[:user_info]['sub']
30
+ end
31
+ end
32
+
33
+ def login_url(_params, _session)
34
+ authorize_url(authorize_url: auth_path)
35
+ end
36
+
37
+ def process_callback(params, _session)
38
+ args = {}.tap do |a|
39
+ a[:code] = params[:code] if params[:code]
40
+ end
41
+
42
+ get_access_token(args, token_url: token_path, token_method: :post)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with discordapp.com
4
+
5
+ class Discord < Base
6
+ include Protocols::Oauth2
7
+
8
+ attr_accessor :auth_path, :scope, :token_url, :user_info_path
9
+
10
+ def initialize
11
+ super
12
+
13
+ @scope = 'identify'
14
+ @site = 'https://discordapp.com/'
15
+ @auth_path = '/api/oauth2/authorize'
16
+ @token_url = '/api/oauth2/token'
17
+ @user_info_path = '/api/users/@me'
18
+ @state = SecureRandom.hex(16)
19
+ end
20
+
21
+ def get_user_hash(access_token)
22
+ response = access_token.get(user_info_path)
23
+ body = JSON.parse(response.body)
24
+ auth_hash(access_token).tap do |h|
25
+ h[:user_info] = body
26
+ h[:uid] = body['id']
27
+ end
28
+ end
29
+
30
+ # calculates and returns the url to which the user should be redirected,
31
+ # to get authenticated at the external provider's site.
32
+ def login_url(_params, _session)
33
+ authorize_url(authorize_url: auth_path)
34
+ end
35
+
36
+ # tries to login the user from access token
37
+ def process_callback(params, _session)
38
+ args = {}.tap do |a|
39
+ a[:code] = params[:code] if params[:code]
40
+ end
41
+ get_access_token(
42
+ args,
43
+ token_url: token_url,
44
+ client_id: @key,
45
+ client_secret: @secret,
46
+ grant_type: 'authorization_code',
47
+ token_method: :post
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
@@ -45,6 +45,7 @@ module Sorcery
45
45
  # tries to login the user from access token
46
46
  def process_callback(params, _session)
47
47
  raise 'Invalid state. Potential Cross Site Forgery' if params[:state] != state
48
+
48
49
  args = {}.tap do |a|
49
50
  a[:code] = params[:code] if params[:code]
50
51
  end
@@ -0,0 +1,73 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with Instagram.com.
4
+ class Instagram < Base
5
+ include Protocols::Oauth2
6
+
7
+ attr_accessor :access_permissions, :token_url,
8
+ :authorization_path, :user_info_path,
9
+ :scope, :user_info_fields
10
+
11
+ def initialize
12
+ super
13
+
14
+ @site = 'https://api.instagram.com'
15
+ @token_url = '/oauth/access_token'
16
+ @authorization_path = '/oauth/authorize/'
17
+ @user_info_path = '/v1/users/self'
18
+ @scope = 'basic'
19
+ end
20
+
21
+ def self.included(base)
22
+ base.extend Sorcery::Providers
23
+ end
24
+
25
+ # provider implements method to build Oauth client
26
+ def login_url(_params, _session)
27
+ authorize_url(token_url: @token_url)
28
+ end
29
+
30
+ # overrides oauth2#authorize_url to allow customized scope.
31
+ def authorize_url(opts = {})
32
+ @scope = access_permissions.present? ? access_permissions.join(' ') : scope
33
+ super(opts.merge(token_url: @token_url))
34
+ end
35
+
36
+ # pass oauth2 param `code` provided by instgrm server
37
+ def process_callback(params, _session)
38
+ args = {}.tap do |a|
39
+ a[:code] = params[:code] if params[:code]
40
+ end
41
+ get_access_token(
42
+ args,
43
+ token_url: @token_url,
44
+ client_id: @key,
45
+ client_secret: @secret
46
+ )
47
+ end
48
+
49
+ # see `user_info_mapping` in config/initializer,
50
+ # given `user_info_mapping` to specify
51
+ # {:db_attribute_name => 'instagram_attr_name'}
52
+ # so that Sorcery can build AR model from attr names
53
+ #
54
+ # NOTE: instead of just getting the user info
55
+ # from the access_token (which already returns them),
56
+ # testing strategy relies on querying user_info_path
57
+ def get_user_hash(access_token)
58
+ call_api_params = {
59
+ access_token: access_token.token,
60
+ client_id: access_token[:client_id]
61
+ }
62
+ response = access_token.get(
63
+ "#{user_info_path}?#{call_api_params.to_param}"
64
+ )
65
+
66
+ user_attrs = {}
67
+ user_attrs[:user_info] = JSON.parse(response.body)['data']
68
+ user_attrs[:uid] = user_attrs[:user_info]['id']
69
+ user_attrs
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with line.com.
4
+ #
5
+ # config.line.key = <key>
6
+ # config.line.secret = <secret>
7
+ # ...
8
+ #
9
+ class Line < Base
10
+ include Protocols::Oauth2
11
+
12
+ attr_accessor :token_url, :user_info_path, :auth_path
13
+
14
+ def initialize
15
+ super
16
+
17
+ @site = 'https://access.line.me'
18
+ @user_info_path = 'https://api.line.me/v2/profile'
19
+ @token_url = 'https://api.line.me/v2/oauth/accessToken'
20
+ @auth_path = 'dialog/oauth/weblogin'
21
+ end
22
+
23
+ def get_user_hash(access_token)
24
+ response = access_token.get(user_info_path)
25
+ auth_hash(access_token).tap do |h|
26
+ h[:user_info] = JSON.parse(response.body)
27
+ h[:uid] = h[:user_info]['userId'].to_s
28
+ end
29
+ end
30
+
31
+ # calculates and returns the url to which the user should be redirected,
32
+ # to get authenticated at the external provider's site.
33
+ def login_url(_params, _session)
34
+ @state = SecureRandom.hex(16)
35
+ authorize_url(authorize_url: auth_path)
36
+ end
37
+ # tries to login the user from access token
38
+ def process_callback(params, _session)
39
+ args = {}.tap do |a|
40
+ a[:code] = params[:code] if params[:code]
41
+ end
42
+
43
+ get_access_token(args, token_url: token_url, token_method: :post)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,65 +1,74 @@
1
1
  module Sorcery
2
2
  module Providers
3
- # This class adds support for OAuth with Linkedin.com.
3
+ # This class adds support for OAuth with LinkedIn.
4
4
  #
5
5
  # config.linkedin.key = <key>
6
6
  # config.linkedin.secret = <secret>
7
7
  # ...
8
8
  #
9
9
  class Linkedin < Base
10
- include Protocols::Oauth
10
+ include Protocols::Oauth2
11
11
 
12
- attr_accessor :authorize_path, :access_permissions, :access_token_path,
13
- :request_token_path, :user_info_fields, :user_info_path
12
+ attr_accessor :auth_url, :scope, :token_url, :user_info_url, :email_info_url
14
13
 
15
14
  def initialize
16
- @configuration = {
17
- site: 'https://api.linkedin.com',
18
- authorize_path: '/uas/oauth/authenticate',
19
- request_token_path: '/uas/oauth/requestToken',
20
- access_token_path: '/uas/oauth/accessToken'
21
- }
22
- @user_info_path = '/v1/people/~'
23
- end
15
+ super
24
16
 
25
- # Override included get_consumer method to provide authorize_path
26
- def get_consumer
27
- # Add access permissions to request token path
28
- @configuration[:request_token_path] += '?scope=' + access_permissions.join('+') unless access_permissions.blank? || @configuration[:request_token_path].include?('?scope=')
29
- ::OAuth::Consumer.new(@key, @secret, @configuration)
17
+ @site = 'https://api.linkedin.com'
18
+ @auth_url = '/oauth/v2/authorization'
19
+ @token_url = '/oauth/v2/accessToken'
20
+ @user_info_url = 'https://api.linkedin.com/v2/me'
21
+ @email_info_url = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))'
22
+ @scope = 'r_liteprofile r_emailaddress'
23
+ @state = SecureRandom.hex(16)
30
24
  end
31
25
 
32
26
  def get_user_hash(access_token)
33
- # Always include id for provider uid and prevent accidental duplication via setting `user_info_field = ['id']` (needed in Sorcery 0.9.1)
34
- info_fields = user_info_fields ? user_info_fields.reject{|n| n == 'id'} : []
35
- fields = info_fields.any? ? 'id,' + info_fields.join(',') : 'id'
36
- response = access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json')
27
+ user_info = get_user_info(access_token)
37
28
 
38
29
  auth_hash(access_token).tap do |h|
39
- h[:user_info] = JSON.parse(response.body)
40
- h[:uid] = h[:user_info]['id'].to_s
30
+ h[:user_info] = user_info
31
+ h[:uid] = h[:user_info]['id']
41
32
  end
42
33
  end
43
34
 
44
35
  # calculates and returns the url to which the user should be redirected,
45
36
  # to get authenticated at the external provider's site.
46
- def login_url(_params, session)
47
- req_token = get_request_token
48
- session[:request_token] = req_token.token
49
- session[:request_token_secret] = req_token.secret
50
- authorize_url(request_token: req_token.token, request_token_secret: req_token.secret)
37
+ def login_url(_params, _session)
38
+ authorize_url(authorize_url: auth_url)
51
39
  end
52
40
 
53
41
  # tries to login the user from access token
54
- def process_callback(params, session)
55
- args = {
56
- oauth_verifier: params[:oauth_verifier],
57
- request_token: session[:request_token],
58
- request_token_secret: session[:request_token_secret]
59
- }
42
+ def process_callback(params, _session)
43
+ args = {}.tap do |a|
44
+ a[:code] = params[:code] if params[:code]
45
+ end
46
+
47
+ get_access_token(args, token_url: token_url, token_method: :post)
48
+ end
49
+
50
+ def get_user_info(access_token)
51
+ response = access_token.get(user_info_url)
52
+ user_info = JSON.parse(response.body)
53
+
54
+ if email_in_scope?
55
+ email = fetch_email(access_token)
56
+
57
+ return user_info.merge(email)
58
+ end
59
+
60
+ user_info
61
+ end
62
+
63
+ def email_in_scope?
64
+ scope.include?('r_emailaddress')
65
+ end
66
+
67
+ def fetch_email(access_token)
68
+ email_response = access_token.get(email_info_url)
69
+ email_info = JSON.parse(email_response.body)['elements'].first
60
70
 
61
- args[:code] = params[:code] if params[:code]
62
- get_access_token(args)
71
+ email_info['handle~']
63
72
  end
64
73
  end
65
74
  end
@@ -9,7 +9,7 @@ module Sorcery
9
9
  class Vk < Base
10
10
  include Protocols::Oauth2
11
11
 
12
- attr_accessor :auth_path, :token_path, :user_info_url, :scope
12
+ attr_accessor :auth_path, :token_path, :user_info_url, :scope, :api_version
13
13
 
14
14
  def initialize
15
15
  super
@@ -28,15 +28,16 @@ module Sorcery
28
28
  access_token: access_token.token,
29
29
  uids: access_token.params['user_id'],
30
30
  fields: user_info_mapping.values.join(','),
31
- scope: scope
31
+ scope: scope,
32
+ v: api_version.to_s
32
33
  }
33
34
 
34
35
  response = access_token.get(user_info_url, params: params)
35
- if user_hash[:user_info] = JSON.parse(response.body)
36
+ if (user_hash[:user_info] = JSON.parse(response.body))
36
37
  user_hash[:user_info] = user_hash[:user_info]['response'][0]
37
38
  user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join(' ')
38
39
 
39
- user_hash[:uid] = user_hash[:user_info]['uid']
40
+ user_hash[:uid] = user_hash[:user_info]['id']
40
41
  user_hash[:user_info]['email'] = access_token.params['email']
41
42
  end
42
43
  user_hash