-
Notifications
You must be signed in to change notification settings - Fork 186
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
Redis sessions #224
base: main
Are you sure you want to change the base?
Redis sessions #224
Conversation
Codecov Report
@@ Coverage Diff @@
## master #224 +/- ##
==========================================
- Coverage 62.23% 59.62% -2.62%
==========================================
Files 50 51 +1
Lines 4067 4225 +158
==========================================
- Hits 2531 2519 -12
- Misses 1349 1510 +161
- Partials 187 196 +9
|
@sporkmonger this is much cleaner than I expected, which is fantastic! I'll take a look at this today/this weekend, provide some review, and kick the tires. Thank you for your work on this effort, it is a fantastic set of functionality. I (we) greatly appreciate your contributions! |
Sure thing. I had some trouble figuring out how to mock the Redis client, which is why there's a bit of a coverage drop I believe. |
@@ -35,6 +38,9 @@ import ( | |||
// PROVIDER_*_CLIENT_SECRET | |||
// PROVIDER_*_SCOPE | |||
// | |||
// PROVIDER_*_AZURE_TENANT | |||
// PROVIDER_*_AZURE_PROMPT |
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.
This snuck in somehow, not sure how critical it is to keep this isolated since this PR is the main blocker for Azure, but I can remove if needed.
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.
Definitely not critical to remove, but I think I'd personally prefer if it was pulled back into the Azure PR and removed here, if that's ok!
@jphines Any updates? |
@sporkmonger it seems there currently is a conflict, could you resolve it ? @jphines Do you have time to review this ? |
My apologizes, I've failed to prioritize review here appropriately. I've asked @Jusshersmith to review these changes in light of my inability to find the time to do so. |
Yeah, I'll try to address the conflict shortly. |
@@ -84,6 +90,9 @@ func DefaultAuthConfig() Configuration { | |||
Secure: true, | |||
HTTPOnly: true, | |||
}, | |||
RedisConfig: RedisConfig{ | |||
UseSentinel: false, |
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.
To clarify: is this just included for explicitness around this setting? (Unless I'm mistaken, it's not strictly required, right?)
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.
Yes, that's correct. It's been awhile but I think it might fail if the RedisConfig
struct doesn't get initialized, but the UseSentinel
default is the false
zero value.
} else { | ||
opts = append(opts, SetCookieStore(config.SessionConfig, idpSlug)) | ||
} | ||
authenticator, err := NewAuthenticator(config, opts...) |
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.
What do you think about maybe creating a new SetSessionStore
function that houses this logic, which would be passed in with the slice of functions to NewAuthenticator
?
Which either essentially calls the above, or returns one of the SetRedisStore
or SetCookieStore
funcs?
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.
However, this kind of pattern is used elsewhere in the codebase! 🤔
@@ -35,6 +38,9 @@ import ( | |||
// PROVIDER_*_CLIENT_SECRET | |||
// PROVIDER_*_SCOPE | |||
// | |||
// PROVIDER_*_AZURE_TENANT | |||
// PROVIDER_*_AZURE_PROMPT |
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.
Definitely not critical to remove, but I think I'd personally prefer if it was pulled back into the Azure PR and removed here, if that's ok!
setSessionStore := SetCookieStore | ||
if opts.RedisConnectionURL != "" || opts.RedisUseSentinel { | ||
setSessionStore = SetRedisStore | ||
} |
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.
This is nice and concise, but I wonder if it's worth standardising to match the other similar use cases above, e.g:
if opts.RedisConnectionURL != "" || opts.RedisUseSentinel {
optFuncs = append(optFuncs, SetRedisStore(opts)
} else {
optFuncs = append(optFuncs, SetCookieStore(opts)
}
Or, at least closer to whatever method we end up using on the authenticator side to set the session store.
CheckFunc: func(c Configuration, t *testing.T) { | ||
redisConf := c.SessionConfig.RedisConfig | ||
assertEq([]string{"redis://master.example.com:6379"}, redisConf.ConnectionURLs, t) | ||
}, |
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 wonder if it's also worth adding in a call to RedisConfig.Validate()
and/or RedisConfig.Enabled()
here? It seems like it could be included fairly easily, and I think would help get the error cases within them tested.
This looks great @sporkmonger, and thank you for your patience! I've left some comments to start with! |
Any updates ? |
Guys ? |
It's taken long enough that... we ended up moving from Azure AD to Okta in the meantime. |
😭 |
Thank you for all of your great work on this @sporkmonger! This seems like it could still be an extremely valuable addition, and would be a shame for all of this work to go to waste. I don't think we at BuzzFeed necessarily have the capacity to push it over the finish line in the immediate future, but I think we can keep this open for a while still to allow someone to pick it up if possible -- providing you're happy with that @sporkmonger. |
Totally. My bandwidth for stuff like this goes up and down a lot, but the next time I've got some spare cycles I'll try to get this addressed. If someone else wants to pick it up and get it over the finish line, I'm equally OK with that too. |
Problem
Session state can become quite large and can easily exceed the maximum cookie size. I previously tried to fix it with #150 but that proved to be a bad solution prone to failures.
Solution
This introduces optional support for a Redis backend for sessions. Setting a
SESSION_REDIS_CONNECTION
environment variable to e.g.redis://primary.sso-sessions.example-cloud.com:6379
will switch the session backend from cookie sessions to redis sessions. The default remains cookie sessions.Notes
This also avoids the issue of large amounts of session state being retransmitted on every request. Session state is encrypted using the existing cipher mechanism and session keys. As discussed in #158, I recommend renaming the cipher key configuration name to be more generic, but this PR doesn't make that change. This PR does not use the ticket system mentioned in #158, and instead opts for the simpler approach of encrypting using the session secret held on the auth server. I have tested this PR in conjunction with #118 and it works well.