sorcery 0.13.0 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/.github/workflows/ruby.yml +49 -0
- data/.rubocop.yml +2 -2
- data/.rubocop_todo.yml +157 -1
- data/CHANGELOG.md +49 -0
- data/CODE_OF_CONDUCT.md +14 -0
- data/Gemfile +1 -1
- data/README.md +4 -4
- data/Rakefile +3 -1
- data/SECURITY.md +19 -0
- data/gemfiles/rails_52.gemfile +7 -0
- data/gemfiles/rails_60.gemfile +7 -0
- data/lib/generators/sorcery/helpers.rb +4 -0
- data/lib/generators/sorcery/templates/initializer.rb +111 -85
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +5 -5
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +4 -4
- data/lib/generators/sorcery/templates/migration/core.rb +4 -4
- data/lib/generators/sorcery/templates/migration/external.rb +3 -3
- data/lib/generators/sorcery/templates/migration/magic_login.rb +4 -4
- data/lib/generators/sorcery/templates/migration/remember_me.rb +3 -3
- data/lib/generators/sorcery/templates/migration/reset_password.rb +5 -5
- data/lib/generators/sorcery/templates/migration/user_activation.rb +4 -4
- data/lib/sorcery/adapters/active_record_adapter.rb +2 -2
- data/lib/sorcery/controller.rb +4 -1
- data/lib/sorcery/controller/config.rb +6 -6
- data/lib/sorcery/controller/submodules/activity_logging.rb +5 -3
- data/lib/sorcery/controller/submodules/external.rb +4 -1
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +2 -1
- data/lib/sorcery/controller/submodules/session_timeout.rb +2 -0
- data/lib/sorcery/crypto_providers/aes256.rb +1 -1
- data/lib/sorcery/crypto_providers/bcrypt.rb +6 -1
- data/lib/sorcery/engine.rb +7 -1
- data/lib/sorcery/model.rb +6 -5
- data/lib/sorcery/model/config.rb +5 -0
- data/lib/sorcery/model/submodules/magic_login.rb +7 -4
- data/lib/sorcery/model/submodules/reset_password.rb +6 -2
- data/lib/sorcery/providers/battlenet.rb +51 -0
- data/lib/sorcery/providers/discord.rb +52 -0
- data/lib/sorcery/providers/line.rb +63 -0
- data/lib/sorcery/providers/linkedin.rb +45 -36
- data/lib/sorcery/providers/vk.rb +1 -1
- data/lib/sorcery/version.rb +1 -1
- data/sorcery.gemspec +5 -6
- data/spec/controllers/controller_oauth2_spec.rb +41 -6
- data/spec/controllers/controller_oauth_spec.rb +6 -0
- data/spec/controllers/controller_remember_me_spec.rb +15 -12
- data/spec/controllers/controller_spec.rb +11 -1
- data/spec/providers/example_provider_spec.rb +17 -0
- data/spec/providers/example_spec.rb +17 -0
- data/spec/rails_app/app/assets/config/manifest.js +1 -0
- data/spec/rails_app/app/controllers/application_controller.rb +2 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +69 -1
- data/spec/rails_app/config/routes.rb +10 -0
- data/spec/shared_examples/user_reset_password_shared_examples.rb +18 -2
- data/spec/shared_examples/user_shared_examples.rb +63 -0
- data/spec/sorcery_crypto_providers_spec.rb +60 -0
- data/spec/support/migration_helper.rb +12 -2
- data/spec/support/providers/example.rb +11 -0
- data/spec/support/providers/example_provider.rb +11 -0
- metadata +25 -15
- data/.travis.yml +0 -38
- data/gemfiles/active_record_rails_40.gemfile +0 -6
- data/gemfiles/active_record_rails_41.gemfile +0 -6
- 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 :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
6
|
-
add_column :<%=
|
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 :<%=
|
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 :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
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 :<%=
|
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 :<%=
|
4
|
-
t.string :email, :
|
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 :
|
8
|
+
t.timestamps null: false
|
9
9
|
end
|
10
10
|
|
11
|
-
add_index :<%=
|
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 :<%=
|
5
|
-
t.string :provider, :uid, :
|
4
|
+
t.integer :<%= tableized_model_class.singularize %>_id, null: false
|
5
|
+
t.string :provider, :uid, null: false
|
6
6
|
|
7
|
-
t.timestamps :
|
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 :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
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 :<%=
|
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 :<%=
|
4
|
-
add_column :<%=
|
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 :<%=
|
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 :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
6
|
-
add_column :<%=
|
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 :<%=
|
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 :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
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 :<%=
|
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)
|
data/lib/sorcery/controller.rb
CHANGED
@@ -25,7 +25,10 @@ module Sorcery
|
|
25
25
|
def require_login
|
26
26
|
return if logged_in?
|
27
27
|
|
28
|
-
|
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
|
-
|
34
|
-
Config.after_login
|
35
|
-
Config.
|
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.
|
43
|
+
@#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.classify).new
|
41
44
|
end
|
42
45
|
RUBY
|
43
46
|
end
|
@@ -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
|
@@ -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)
|
data/lib/sorcery/engine.rb
CHANGED
@@ -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
|
-
#
|
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
|
209
|
+
return unless mail.respond_to?(config.email_delivery_method)
|
209
210
|
|
210
211
|
mail.send(config.email_delivery_method)
|
211
212
|
end
|
data/lib/sorcery/model/config.rb
CHANGED
@@ -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
|
-
|
57
|
-
|
58
|
-
|
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
|
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:
|
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
|