Align session codec max-age with the 7-day cap (closes #108)
All checks were successful
check / check (push) Successful in 3m5s
All checks were successful
check / check (push) Successful in 3m5s
sessions.NewCookieStore gives its securecookie codecs a 30-day max age, and assigning store.Options never touches Codecs. The store therefore decoded a cookie up to 30 days old while the cookie attribute and the server-side expiry check both said 7 days, so a refactor of that check -- or a second decode path that skips IsAuthenticated -- would silently reopen a 30-day window. Build the store through store.MaxAge, which sets Options.MaxAge and propagates to every codec. The store is now built by newStore, the one place a store is constructed; Regenerate shares its cookie attributes via cookieOptions. Tests: - Two codec tests decode a re-stamped cookie an hour inside and an hour outside the cap. They only exercise Session.Get, so they pin the codec: reverting the fix fails the rejection test whether or not the server-side expiry check is present. - The lazy-refresh bound is pinned two-sidedly, so the documented "expires up to 10% early, never late" guarantee now fails the suite if idleRefreshDivisor drifts in either direction. Also scopes the RequireAuth save error to a distinct saveErr variable instead of reusing the outer err. It is a plain assignment rather than an inline "if saveErr := ...; saveErr != nil", because the repo's noinlineerr linter rejects that form. Behaviour is unchanged; this is scoping hygiene, not a bug fix. Two README claims are corrected as well: the idle timeout is disabled by any non-positive value, not only 0, and deploying the two-clock expiry logs existing sessions out once because they carry no timestamps. #108
This commit is contained in:
@@ -39,6 +39,19 @@ func (c *fakeClock) Advance(d time.Duration) {
|
||||
c.t = c.t.Add(d)
|
||||
}
|
||||
|
||||
// testKey returns the fixed session key the tests sign with. The
|
||||
// codec tests re-sign cookies with it, so it must be the same key the
|
||||
// store was built from.
|
||||
func testKey() []byte {
|
||||
key := make([]byte, testKeySize)
|
||||
|
||||
for i := range key {
|
||||
key[i] = byte(i + 42)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// testSession creates a Session with a real cookie store and the
|
||||
// real clock.
|
||||
func testSession(t *testing.T) *session.Session {
|
||||
@@ -59,20 +72,8 @@ func testSessionWithClock(
|
||||
) (*session.Session, *fakeClock) {
|
||||
t.Helper()
|
||||
|
||||
key := make([]byte, testKeySize)
|
||||
|
||||
for i := range key {
|
||||
key[i] = byte(i + 42)
|
||||
}
|
||||
|
||||
store := sessions.NewCookieStore(key)
|
||||
store.Options = &sessions.Options{
|
||||
Path: "/",
|
||||
MaxAge: 86400 * 7,
|
||||
HttpOnly: true,
|
||||
Secure: false,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
}
|
||||
key := testKey()
|
||||
store := session.NewStore(key, false)
|
||||
|
||||
cfg := &config.Config{
|
||||
Environment: config.EnvironmentDev,
|
||||
@@ -645,6 +646,34 @@ func TestTouch_LazyBelowRefreshThreshold(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestTouch_RefreshThresholdIsOneTenthOfIdleWindow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// testRefreshDivisor restates the documented bound independently
|
||||
// of the implementation constant: the idle timestamp is rewritten
|
||||
// once it is a tenth of the idle window old, which is what makes
|
||||
// "expires up to 10% early, never late" true. Both assertions are
|
||||
// needed to pin it -- a larger divisor fails the first, a smaller
|
||||
// one fails the second.
|
||||
const testRefreshDivisor = 10
|
||||
|
||||
threshold := testIdleTimeout / testRefreshDivisor
|
||||
|
||||
s, sess, clock := authenticatedSession(t, testIdleTimeout)
|
||||
|
||||
clock.Advance(threshold - time.Second)
|
||||
assert.False(
|
||||
t, s.Touch(sess),
|
||||
"Touch must not rewrite the session below a tenth of the window",
|
||||
)
|
||||
|
||||
clock.Advance(time.Second)
|
||||
assert.True(
|
||||
t, s.Touch(sess),
|
||||
"Touch must rewrite the session at a tenth of the window",
|
||||
)
|
||||
}
|
||||
|
||||
func TestTouch_UnauthenticatedSessionIsNotRefreshed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user