sorcery 0.13.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
  4. data/.github/workflows/ruby.yml +49 -0
  5. data/.rubocop.yml +2 -2
  6. data/.rubocop_todo.yml +157 -1
  7. data/CHANGELOG.md +49 -0
  8. data/CODE_OF_CONDUCT.md +14 -0
  9. data/Gemfile +1 -1
  10. data/README.md +4 -4
  11. data/Rakefile +3 -1
  12. data/SECURITY.md +19 -0
  13. data/gemfiles/rails_52.gemfile +7 -0
  14. data/gemfiles/rails_60.gemfile +7 -0
  15. data/lib/generators/sorcery/helpers.rb +4 -0
  16. data/lib/generators/sorcery/templates/initializer.rb +111 -85
  17. data/lib/generators/sorcery/templates/migration/activity_logging.rb +5 -5
  18. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +4 -4
  19. data/lib/generators/sorcery/templates/migration/core.rb +4 -4
  20. data/lib/generators/sorcery/templates/migration/external.rb +3 -3
  21. data/lib/generators/sorcery/templates/migration/magic_login.rb +4 -4
  22. data/lib/generators/sorcery/templates/migration/remember_me.rb +3 -3
  23. data/lib/generators/sorcery/templates/migration/reset_password.rb +5 -5
  24. data/lib/generators/sorcery/templates/migration/user_activation.rb +4 -4
  25. data/lib/sorcery/adapters/active_record_adapter.rb +2 -2
  26. data/lib/sorcery/controller.rb +4 -1
  27. data/lib/sorcery/controller/config.rb +6 -6
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +5 -3
  29. data/lib/sorcery/controller/submodules/external.rb +4 -1
  30. data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -0
  31. data/lib/sorcery/controller/submodules/remember_me.rb +2 -1
  32. data/lib/sorcery/controller/submodules/session_timeout.rb +2 -0
  33. data/lib/sorcery/crypto_providers/aes256.rb +1 -1
  34. data/lib/sorcery/crypto_providers/bcrypt.rb +6 -1
  35. data/lib/sorcery/engine.rb +7 -1
  36. data/lib/sorcery/model.rb +6 -5
  37. data/lib/sorcery/model/config.rb +5 -0
  38. data/lib/sorcery/model/submodules/magic_login.rb +7 -4
  39. data/lib/sorcery/model/submodules/reset_password.rb +6 -2
  40. data/lib/sorcery/providers/battlenet.rb +51 -0
  41. data/lib/sorcery/providers/discord.rb +52 -0
  42. data/lib/sorcery/providers/line.rb +63 -0
  43. data/lib/sorcery/providers/linkedin.rb +45 -36
  44. data/lib/sorcery/providers/vk.rb +1 -1
  45. data/lib/sorcery/version.rb +1 -1
  46. data/sorcery.gemspec +5 -6
  47. data/spec/controllers/controller_oauth2_spec.rb +41 -6
  48. data/spec/controllers/controller_oauth_spec.rb +6 -0
  49. data/spec/controllers/controller_remember_me_spec.rb +15 -12
  50. data/spec/controllers/controller_spec.rb +11 -1
  51. data/spec/providers/example_provider_spec.rb +17 -0
  52. data/spec/providers/example_spec.rb +17 -0
  53. data/spec/rails_app/app/assets/config/manifest.js +1 -0
  54. data/spec/rails_app/app/controllers/application_controller.rb +2 -0
  55. data/spec/rails_app/app/controllers/sorcery_controller.rb +69 -1
  56. data/spec/rails_app/config/routes.rb +10 -0
  57. data/spec/shared_examples/user_reset_password_shared_examples.rb +18 -2
  58. data/spec/shared_examples/user_shared_examples.rb +63 -0
  59. data/spec/sorcery_crypto_providers_spec.rb +60 -0
  60. data/spec/support/migration_helper.rb +12 -2
  61. data/spec/support/providers/example.rb +11 -0
  62. data/spec/support/providers/example_provider.rb +11 -0
  63. metadata +25 -15
  64. data/.travis.yml +0 -38
  65. data/gemfiles/active_record_rails_40.gemfile +0 -6
  66. data/gemfiles/active_record_rails_41.gemfile +0 -6
  67. data/gemfiles/active_record_rails_42.gemfile +0 -6
@@ -1,10 +1,10 @@
1
1
  class SorceryActivityLogging < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, :default => nil
6
- add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, :default => nil
3
+ add_column :<%= tableized_model_class %>, :last_login_at, :datetime, default: nil
4
+ add_column :<%= tableized_model_class %>, :last_logout_at, :datetime, default: nil
5
+ add_column :<%= tableized_model_class %>, :last_activity_at, :datetime, default: nil
6
+ add_column :<%= tableized_model_class %>, :last_login_from_ip_address, :string, default: nil
7
7
 
8
- add_index :<%= model_class_name.tableize %>, [:last_logout_at, :last_activity_at]
8
+ add_index :<%= tableized_model_class %>, [:last_logout_at, :last_activity_at]
9
9
  end
10
10
  end
@@ -1,9 +1,9 @@
1
1
  class SorceryBruteForceProtection < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, :default => 0
4
- add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :unlock_token, :string, :default => nil
3
+ add_column :<%= tableized_model_class %>, :failed_logins_count, :integer, default: 0
4
+ add_column :<%= tableized_model_class %>, :lock_expires_at, :datetime, default: nil
5
+ add_column :<%= tableized_model_class %>, :unlock_token, :string, default: nil
6
6
 
7
- add_index :<%= model_class_name.tableize %>, :unlock_token
7
+ add_index :<%= tableized_model_class %>, :unlock_token
8
8
  end
9
9
  end
@@ -1,13 +1,13 @@
1
1
  class SorceryCore < <%= migration_class_name %>
2
2
  def change
3
- create_table :<%= model_class_name.tableize %> do |t|
4
- t.string :email, :null => false
3
+ create_table :<%= tableized_model_class %> do |t|
4
+ t.string :email, null: false
5
5
  t.string :crypted_password
6
6
  t.string :salt
7
7
 
8
- t.timestamps :null => false
8
+ t.timestamps null: false
9
9
  end
10
10
 
11
- add_index :<%= model_class_name.tableize %>, :email, unique: true
11
+ add_index :<%= tableized_model_class %>, :email, unique: true
12
12
  end
13
13
  end
@@ -1,10 +1,10 @@
1
1
  class SorceryExternal < <%= migration_class_name %>
2
2
  def change
3
3
  create_table :authentications do |t|
4
- t.integer :<%= model_class_name.tableize.singularize %>_id, :null => false
5
- t.string :provider, :uid, :null => false
4
+ t.integer :<%= tableized_model_class.singularize %>_id, null: false
5
+ t.string :provider, :uid, null: false
6
6
 
7
- t.timestamps :null => false
7
+ t.timestamps null: false
8
8
  end
9
9
 
10
10
  add_index :authentications, [:provider, :uid]
@@ -1,9 +1,9 @@
1
1
  class SorceryMagicLogin < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, :default => nil
3
+ add_column :<%= tableized_model_class %>, :magic_login_token, :string, default: nil
4
+ add_column :<%= tableized_model_class %>, :magic_login_token_expires_at, :datetime, default: nil
5
+ add_column :<%= tableized_model_class %>, :magic_login_email_sent_at, :datetime, default: nil
6
6
 
7
- add_index :<%= model_class_name.tableize %>, :magic_login_token
7
+ add_index :<%= tableized_model_class %>, :magic_login_token
8
8
  end
9
9
  end
@@ -1,8 +1,8 @@
1
1
  class SorceryRememberMe < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, :default => nil
3
+ add_column :<%= tableized_model_class %>, :remember_me_token, :string, default: nil
4
+ add_column :<%= tableized_model_class %>, :remember_me_token_expires_at, :datetime, default: nil
5
5
 
6
- add_index :<%= model_class_name.tableize %>, :remember_me_token
6
+ add_index :<%= tableized_model_class %>, :remember_me_token
7
7
  end
8
8
  end
@@ -1,10 +1,10 @@
1
1
  class SorceryResetPassword < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, :default => nil
6
- add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, :default => 0
3
+ add_column :<%= tableized_model_class %>, :reset_password_token, :string, default: nil
4
+ add_column :<%= tableized_model_class %>, :reset_password_token_expires_at, :datetime, default: nil
5
+ add_column :<%= tableized_model_class %>, :reset_password_email_sent_at, :datetime, default: nil
6
+ add_column :<%= tableized_model_class %>, :access_count_to_reset_password_page, :integer, default: 0
7
7
 
8
- add_index :<%= model_class_name.tableize %>, :reset_password_token
8
+ add_index :<%= tableized_model_class %>, :reset_password_token
9
9
  end
10
10
  end
@@ -1,9 +1,9 @@
1
1
  class SorceryUserActivation < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :activation_state, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :activation_token, :string, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, :default => nil
3
+ add_column :<%= tableized_model_class %>, :activation_state, :string, default: nil
4
+ add_column :<%= tableized_model_class %>, :activation_token, :string, default: nil
5
+ add_column :<%= tableized_model_class %>, :activation_token_expires_at, :datetime, default: nil
6
6
 
7
- add_index :<%= model_class_name.tableize %>, :activation_token
7
+ add_index :<%= tableized_model_class %>, :activation_token
8
8
  end
9
9
  end
@@ -12,7 +12,7 @@ module Sorcery
12
12
 
13
13
  def save(options = {})
14
14
  mthd = options.delete(:raise_on_failure) ? :save! : :save
15
- @model.send(mthd, options)
15
+ @model.send(mthd, **options)
16
16
  end
17
17
 
18
18
  def increment(field)
@@ -35,7 +35,7 @@ module Sorcery
35
35
  end
36
36
 
37
37
  def define_callback(time, event, method_name, options = {})
38
- @klass.send "#{time}_#{event}", method_name, options.slice(:if, :on)
38
+ @klass.send "#{time}_#{event}", method_name, **options.slice(:if, :on)
39
39
  end
40
40
 
41
41
  def find_by_oauth_credentials(provider, uid)
@@ -25,7 +25,10 @@ module Sorcery
25
25
  def require_login
26
26
  return if logged_in?
27
27
 
28
- session[:return_to_url] = request.url if Config.save_return_to_url && request.get? && !request.xhr?
28
+ if Config.save_return_to_url && request.get? && !request.xhr? && !request.format.json?
29
+ session[:return_to_url] = request.url
30
+ end
31
+
29
32
  send(Config.not_authenticated_action)
30
33
  end
31
34
 
@@ -25,12 +25,12 @@ module Sorcery
25
25
  :@user_class => nil,
26
26
  :@submodules => [],
27
27
  :@not_authenticated_action => :not_authenticated,
28
- :@login_sources => [],
29
- :@after_login => [],
30
- :@after_failed_login => [],
31
- :@before_logout => [],
32
- :@after_logout => [],
33
- :@after_remember_me => [],
28
+ :@login_sources => Set.new,
29
+ :@after_login => Set.new,
30
+ :@after_failed_login => Set.new,
31
+ :@before_logout => Set.new,
32
+ :@after_logout => Set.new,
33
+ :@after_remember_me => Set.new,
34
34
  :@save_return_to_url => true,
35
35
  :@cookie_domain => nil
36
36
  }
@@ -30,9 +30,11 @@ module Sorcery
30
30
  end
31
31
  merge_activity_logging_defaults!
32
32
  end
33
- Config.after_login << :register_login_time_to_db
34
- Config.after_login << :register_last_ip_address
35
- Config.before_logout << :register_logout_time_to_db
33
+
34
+ Config.after_login << :register_login_time_to_db
35
+ Config.after_login << :register_last_ip_address
36
+ Config.before_logout << :register_logout_time_to_db
37
+
36
38
  base.after_action :register_last_activity_time_to_db
37
39
  end
38
40
 
@@ -25,6 +25,9 @@ module Sorcery
25
25
  require 'sorcery/providers/microsoft'
26
26
  require 'sorcery/providers/instagram'
27
27
  require 'sorcery/providers/auth0'
28
+ require 'sorcery/providers/line'
29
+ require 'sorcery/providers/discord'
30
+ require 'sorcery/providers/battlenet'
28
31
 
29
32
  Config.module_eval do
30
33
  class << self
@@ -37,7 +40,7 @@ module Sorcery
37
40
  providers.each do |name|
38
41
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
39
42
  def self.#{name}
40
- @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new
43
+ @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.classify).new
41
44
  end
42
45
  RUBY
43
46
  end
@@ -19,6 +19,7 @@ module Sorcery
19
19
  end
20
20
  merge_http_basic_auth_defaults!
21
21
  end
22
+
22
23
  Config.login_sources << :login_from_basic_auth
23
24
  end
24
25
 
@@ -17,6 +17,7 @@ module Sorcery
17
17
  end
18
18
  merge_remember_me_defaults!
19
19
  end
20
+
20
21
  Config.login_sources << :login_from_cookie
21
22
  Config.before_logout << :forget_me!
22
23
  end
@@ -54,7 +55,7 @@ module Sorcery
54
55
  # and logs the user in if found.
55
56
  # Runs as a login source. See 'current_user' method for how it is used.
56
57
  def login_from_cookie
57
- user = cookies.signed[:remember_me_token] && user_class.sorcery_adapter.find_by_remember_me_token(cookies.signed[:remember_me_token])
58
+ user = cookies.signed[:remember_me_token] && user_class.sorcery_adapter.find_by_remember_me_token(cookies.signed[:remember_me_token]) if defined? cookies
58
59
  if user && user.has_remember_me_token?
59
60
  set_remember_me_cookie!(user)
60
61
  session[:user_id] = user.id.to_s
@@ -23,8 +23,10 @@ module Sorcery
23
23
  end
24
24
  merge_session_timeout_defaults!
25
25
  end
26
+
26
27
  Config.after_login << :register_login_time
27
28
  Config.after_remember_me << :register_login_time
29
+
28
30
  base.prepend_before_action :validate_session
29
31
  end
30
32
 
@@ -29,7 +29,7 @@ module Sorcery
29
29
 
30
30
  def matches?(crypted, *tokens)
31
31
  decrypt(crypted) == tokens.join
32
- rescue OpenSSL::CipherError
32
+ rescue OpenSSL::Cipher::CipherError
33
33
  false
34
34
  end
35
35
 
@@ -40,6 +40,10 @@ module Sorcery
40
40
  # You are good to go!
41
41
  class BCrypt
42
42
  class << self
43
+ # Setting the option :pepper allows users to append an app-specific secret token.
44
+ # Basically it's equivalent to :salt_join_token option, but have a different name to ensure
45
+ # backward compatibility in generating/matching passwords.
46
+ attr_accessor :pepper
43
47
  # This is the :cost option for the BCrpyt library.
44
48
  # The higher the cost the more secure it is and the longer is take the generate a hash. By default this is 10.
45
49
  # Set this to whatever you want, play around with it to get that perfect balance between
@@ -77,12 +81,13 @@ module Sorcery
77
81
 
78
82
  def reset!
79
83
  @cost = 10
84
+ @pepper = ''
80
85
  end
81
86
 
82
87
  private
83
88
 
84
89
  def join_tokens(tokens)
85
- tokens.flatten.join
90
+ tokens.flatten.join.concat(pepper.to_s) # make sure to add pepper in case tokens have only one element
86
91
  end
87
92
 
88
93
  def new_from_hash(hash)
@@ -7,12 +7,18 @@ module Sorcery
7
7
  class Engine < Rails::Engine
8
8
  config.sorcery = ::Sorcery::Controller::Config
9
9
 
10
+ # TODO: Should this include a modified version of the helper methods?
10
11
  initializer 'extend Controller with sorcery' do
11
- # TODO: Should this include a modified version of the helper methods?
12
+ # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks
13
+ # applications due to undefined method errors.
14
+ # ActiveSupport.on_load(:action_controller_api) do
12
15
  if defined?(ActionController::API)
13
16
  ActionController::API.send(:include, Sorcery::Controller)
14
17
  end
15
18
 
19
+ # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks
20
+ # applications due to undefined method errors.
21
+ # ActiveSupport.on_load(:action_controller_base) do
16
22
  if defined?(ActionController::Base)
17
23
  ActionController::Base.send(:include, Sorcery::Controller)
18
24
  ActionController::Base.helper_method :current_user
data/lib/sorcery/model.rb CHANGED
@@ -102,10 +102,6 @@ module Sorcery
102
102
 
103
103
  set_encryption_attributes
104
104
 
105
- unless user.valid_password?(credentials[1])
106
- return authentication_response(user: user, failure: :invalid_password, &block)
107
- end
108
-
109
105
  if user.respond_to?(:active_for_authentication?) && !user.active_for_authentication?
110
106
  return authentication_response(user: user, failure: :inactive, &block)
111
107
  end
@@ -118,6 +114,10 @@ module Sorcery
118
114
  end
119
115
  end
120
116
 
117
+ unless user.valid_password?(credentials[1])
118
+ return authentication_response(user: user, failure: :invalid_password, &block)
119
+ end
120
+
121
121
  authentication_response(user: user, return_value: user, &block)
122
122
  end
123
123
 
@@ -142,6 +142,7 @@ module Sorcery
142
142
  def set_encryption_attributes
143
143
  @sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches
144
144
  @sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token
145
+ @sorcery_config.encryption_provider.pepper = @sorcery_config.pepper if @sorcery_config.encryption_provider.respond_to?(:pepper) && @sorcery_config.pepper
145
146
  end
146
147
 
147
148
  def add_config_inheritance
@@ -205,7 +206,7 @@ module Sorcery
205
206
  def generic_send_email(method, mailer)
206
207
  config = sorcery_config
207
208
  mail = config.send(mailer).send(config.send(method), self)
208
- return unless defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base
209
+ return unless mail.respond_to?(config.email_delivery_method)
209
210
 
210
211
  mail.send(config.email_delivery_method)
211
212
  end
@@ -12,7 +12,11 @@ module Sorcery
12
12
  attr_accessor :downcase_username_before_authenticating
13
13
  # change default crypted_password attribute.
14
14
  attr_accessor :crypted_password_attribute_name
15
+ # application-specific secret token that is joined with the password and its salt.
16
+ # Currently available with BCrypt (default crypt provider) only.
17
+ attr_accessor :pepper
15
18
  # what pattern to use to join the password with the salt
19
+ # APPLICABLE TO MD5, SHA1, SHA256, SHA512. Other crypt providers (incl. BCrypt) ignore this parameter.
16
20
  attr_accessor :salt_join_token
17
21
  # change default salt attribute.
18
22
  attr_accessor :salt_attribute_name
@@ -57,6 +61,7 @@ module Sorcery
57
61
  :@encryption_provider => CryptoProviders::BCrypt,
58
62
  :@custom_encryption_provider => nil,
59
63
  :@encryption_key => nil,
64
+ :@pepper => '',
60
65
  :@salt_join_token => '',
61
66
  :@salt_attribute_name => :salt,
62
67
  :@stretches => nil,
@@ -52,10 +52,13 @@ module Sorcery
52
52
  module ClassMethods
53
53
  # Find user by token, also checks for expiration.
54
54
  # Returns the user if token found and is valid.
55
- def load_from_magic_login_token(token)
56
- token_attr_name = @sorcery_config.magic_login_token_attribute_name
57
- token_expiration_date_attr = @sorcery_config.magic_login_token_expires_at_attribute_name
58
- load_from_token(token, token_attr_name, token_expiration_date_attr)
55
+ def load_from_magic_login_token(token, &block)
56
+ load_from_token(
57
+ token,
58
+ @sorcery_config.magic_login_token_attribute_name,
59
+ @sorcery_config.magic_login_token_expires_at_attribute_name,
60
+ &block
61
+ )
59
62
  end
60
63
 
61
64
  protected
@@ -124,10 +124,14 @@ module Sorcery
124
124
  end
125
125
 
126
126
  # Clears token and tries to update the new password for the user.
127
- def change_password!(new_password)
127
+ def change_password(new_password, raise_on_failure: false)
128
128
  clear_reset_password_token
129
129
  send(:"#{sorcery_config.password_attribute_name}=", new_password)
130
- sorcery_adapter.save raise_on_failure: true
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)
131
135
  end
132
136
 
133
137
  protected
@@ -0,0 +1,51 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with BattleNet
4
+
5
+ class Battlenet < 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 = 'openid'
14
+ @site = 'https://eu.battle.net/'
15
+ @auth_path = '/oauth/authorize'
16
+ @token_url = '/oauth/token'
17
+ @user_info_path = '/oauth/userinfo'
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[:battletag] = body['battletag']
27
+ h[:uid] = body['id']
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
+ authorize_url(authorize_url: auth_path)
35
+ end
36
+
37
+ # tries to login the user from access token
38
+ def process_callback(params, _session)
39
+ args = { code: params[:code] }
40
+ get_access_token(
41
+ args,
42
+ token_url: token_url,
43
+ client_id: @key,
44
+ client_secret: @secret,
45
+ grant_type: 'authorization_code',
46
+ token_method: :post
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end