-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configurable token randomness #67
Conversation
Should we set a minimum? |
@Ch4s3 good to have but I'm not sure how long minimum should be 🤔 |
lib/sorcery/model/temporary_token.rb
Outdated
@@ -12,7 +12,7 @@ def self.included(base) | |||
|
|||
# Random code, used for salt and temporary tokens. | |||
def self.generate_random_token | |||
SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz') | |||
SecureRandom.urlsafe_base64(Controller::Config.token_randomness).tr('lIO0', 'sxyz') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is not safety enough.
can you consider to do something like below?
SecureRandom.urlsafe_base64([15, Controller::Config.token_randomness].max).tr('lIO0', 'sxyz')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and there may be one more thing, you can calculate real length of the result value if you want.
real_length = (15 * 3) / 4
SecureRandom.urlsafe_base64(real_length).tr('lIO0', 'sxyz').size
=> 15
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[15, Controller::Config.token_randomness].max
It is weird that it isn't set the specified value by user. So, I think the specified value should always be set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and there may be one more thing, you can calculate real length of the result value if you want.
I think it is difficult to control real length:
[3] pry(main)> (15..100).select {|i| SecureRandom.urlsafe_base64((i * 3) / 4).size != i }
=> [17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is not safety enough.
can you consider to do something like below?
I agree with setting minimum randomness as 15 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We (@kyuden and I) talked in person and we think the specified value always should be respected so I won't implement minimum value.
lib/sorcery/controller/config.rb
Outdated
@@ -12,6 +12,8 @@ class << self | |||
attr_accessor :save_return_to_url | |||
# set domain option for cookies | |||
attr_accessor :cookie_domain | |||
# Set token randomness | |||
attr_accessor :token_randomness |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorcery:Controler::Config
is configuration used for Sorcery::Controller
. But token_randomness
is used for Sorcery::Model::TemporaryToken
So, It is better to set it with Sorcery:Model::Config
like encryption_algorithm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't access Sorcery::Model::Config
instance directly because Sorcery::Model::TemporaryToken
is a helper module and isn't included into user_class
.
If you really want to do that, the code will be the following:
Sorcery::Controller::Config.user_class.to_s.constantize.sorcery_config.token_randomness
Should I keep the setting in Sorcery:Controler::Config
or do such trick?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 IMO, it seems that Sorcery::Model::Config
is singleton so it is not bad to change implementation to access the instance as same as Sorcery::Controller::Config
Sorcery::Model::Config.token_randomness #=> 15
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TemporaryToken
is under Model
module so I'll move this setting to Sorcery::Model::Config
36698d7
to
19143fc
Compare
@kyuden @akinrt Could you review this PR again? 🙏 |
# The length of the result string is about 4/3 of `token_randomness`. | ||
# Default: `15` | ||
# | ||
# config.token_randomness = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/config.token_randomness =
/user.token_randomness =
/
@mtsmfm Could you actually check that not only the test code but also this setting works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
19143fc
to
d0e403a
Compare
@athix I approved this PR. Could you take a look at this? |
@athix Are you busy in this week? |
@kyuden Sorry, yeah I've been busy with some personal stuff and work. I'll try to get a review for this done as soon as I can. |
@athix thx! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but I'd like to see if we can make the sorcery_config call consistent with other similar calls before rolling this out.
lib/sorcery/model/temporary_token.rb
Outdated
@@ -12,7 +12,7 @@ def self.included(base) | |||
|
|||
# Random code, used for salt and temporary tokens. | |||
def self.generate_random_token | |||
SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz') | |||
SecureRandom.urlsafe_base64(Sorcery::Controller::Config.user_class.to_s.constantize.sorcery_config.token_randomness).tr('lIO0', 'sxyz') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this randomization method is used globally throughout Sorcery, so I'm a bit hesitant tying the config specifically to the user. Also, it seems a bit odd that we'd have to hook through the Controller config to get the value, instead of being able to call sorcery_config directly. Looks like getting that to work isn't exactly trivial though because the generate method is outside of ClassMethods/InstanceMethods.
I'll keep digging around and see if I can figure out what's going on in there.
@@ -0,0 +1,27 @@ | |||
require 'spec_helper' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay! Thanks for writing tests for the new functionality. 👍
@@ -7,12 +7,14 @@ module Model | |||
# such as reseting password and activating the user by email. | |||
module TemporaryToken | |||
def self.included(base) | |||
# FIXME: This may not be the ideal way of passing sorcery_config to generate_random_token. | |||
@sorcery_config = base.sorcery_config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mildly worried that this might cause @sorcery_config
to be unintentionally exposed, but not familiar enough with how ruby inheritance/privacy works to be sure. This is how it gets saved in lib/sorcery/model.rb
however.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sorcery_config
is owned by TemporaryToken
.
It will be overridden if we include TemporaryToken
.
module Foo
def self.included(base)
@var = base.name
end
def self.foo
puts @var
end
end
class Bar
include Foo
end
Foo.foo #=> Bar
puts Foo.instance_variable_get(:@var) #=> Bar
class Baz
include Foo
end
Foo.foo #=> Baz
puts Foo.instance_variable_get(:@var) #=> Baz
Is this intentional behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, Sorcery::Model is the only place where TemporaryToken is/should be included. Based on your description, it sounds like it should be working exactly as intended. What I was worried about was more along the lines of @sorcery_config
accidentally becoming available inside of a controller/model class.
For example:
class User < ApplicationRecord
authenticates_with_sorcery!
def some_method
# @sorcery_config should not be available here!
@sorcery_config = nil # This hopefully shouldn't break things either!
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for late reply 💦
Don't worry, User instance can't override @sorcery_config
module Foo
def self.included(base)
@var = base.name
end
def self.foo
@var
end
end
class Bar
include Foo
def bar
@var
end
def bar=(var)
@var = var
end
end
p Foo.foo #=> "Bar"
p Foo.instance_variable_get(:@var) #=> "Bar"
bar = Bar.new
p bar.bar #=> nil
bar.bar = :bar
p bar.bar #=> :bar
p Foo.foo #=> Bar
p Foo.instance_variable_get(:@var) #=> Bar
@Ch4s3 I think this is probably ready to go now, mind giving this a once over? |
I'm planning on cutting a new release sometime in the next couple weeks, if I don't hear any objections before then I'll merge this in and include it for that release. |
To increase security, I want to change token randomness by configuration 🔑