Skip to content

Commit

Permalink
fix(session/data): next refresh time should account for inactivity ti…
Browse files Browse the repository at this point in the history
…meouts

The default auto-refresh behaviour occurs 5 minutes before tokens
expire, at the earliest. Without inactivity however, tokens are still
refreshed at any point after this, as long as the session has not ended.

This however, means that refreshes don't occur often enough when inactivity
timeouts are enabled. In practice, the session is only refreshed if a
request is received within the 5 minute leeway window between a token's expiry
and the inactivity timeout.

This commit will apply auto-refreshes at the half-life of the inactivity
timeout instead, so that users' sessions and timeouts are properly
extended on activity.
  • Loading branch information
tronghn committed Jan 22, 2025
1 parent c1dd4f1 commit f2def8d
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 24 deletions.
12 changes: 12 additions & 0 deletions pkg/session/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,18 @@ func (in *Metadata) NextRefresh() time.Time {
// subtract the leeway to ensure that we refresh before expiry
next := in.Tokens.ExpireAt.Add(-RefreshLeeway)

// if inactivity is enabled...
timeout := in.Session.TimeoutAt
if !timeout.IsZero() {
// ...refresh at the half-life between the last refresh and the timeout
lastRefresh := in.Tokens.RefreshedAt
halfLife := lastRefresh.Add(timeout.Sub(lastRefresh) / 2)

if halfLife.Before(next) {
next = halfLife
}
}

// try to refresh at the first opportunity if the next refresh is in the past
if next.Before(time.Now()) {
return in.RefreshCooldown()
Expand Down
80 changes: 56 additions & 24 deletions pkg/session/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,30 +122,6 @@ func TestMetadata_IsRefreshOnCooldown(t *testing.T) {
})
}

func TestMetadata_NextRefresh(t *testing.T) {
t.Run("delta to last refresh below minimum interval", func(t *testing.T) {
metadata := session.Metadata{
Tokens: session.MetadataTokens{
RefreshedAt: time.Now(),
ExpireAt: time.Now().Add(time.Minute),
},
}

assert.True(t, metadata.IsRefreshOnCooldown())
})

t.Run("delta to last refresh above minimum interval", func(t *testing.T) {
metadata := session.Metadata{
Tokens: session.MetadataTokens{
RefreshedAt: time.Now().Add(-2 * time.Minute),
ExpireAt: time.Now().Add(time.Minute),
},
}

assert.False(t, metadata.IsRefreshOnCooldown())
})
}

func TestMetadata_Refresh(t *testing.T) {
metadata := session.Metadata{
Tokens: session.MetadataTokens{
Expand Down Expand Up @@ -247,6 +223,62 @@ func TestMetadata_ShouldRefresh(t *testing.T) {

assert.True(t, metadata.ShouldRefresh())
})

t.Run("time between last refresh and timeout has passed half-life", func(t *testing.T) {
metadata := session.Metadata{
Session: session.MetadataSession{
TimeoutAt: time.Now().Add(10 * time.Minute),
},
Tokens: session.MetadataTokens{
RefreshedAt: time.Now().Add(-15 * time.Minute),
ExpireAt: time.Now().Add(time.Hour),
},
}

assert.True(t, metadata.ShouldRefresh())
})

t.Run("time between last refresh and timeout has not passed half-life", func(t *testing.T) {
metadata := session.Metadata{
Session: session.MetadataSession{
TimeoutAt: time.Now().Add(10 * time.Minute),
},
Tokens: session.MetadataTokens{
RefreshedAt: time.Now().Add(-5 * time.Minute),
ExpireAt: time.Now().Add(time.Hour),
},
}

assert.False(t, metadata.ShouldRefresh())
})

t.Run("token expiry occurs before inactivity, token is not within expiry range", func(t *testing.T) {
metadata := session.Metadata{
Session: session.MetadataSession{
TimeoutAt: time.Now().Add(2 * time.Hour),
},
Tokens: session.MetadataTokens{
RefreshedAt: time.Now().Add(-5 * time.Minute),
ExpireAt: time.Now().Add(time.Hour),
},
}

assert.False(t, metadata.ShouldRefresh())
})

t.Run("token expiry occurs before inactivity, token is about to expire", func(t *testing.T) {
metadata := session.Metadata{
Session: session.MetadataSession{
TimeoutAt: time.Now().Add(time.Hour),
},
Tokens: session.MetadataTokens{
RefreshedAt: time.Now().Add(-5 * time.Minute),
ExpireAt: time.Now().Add(time.Minute),
},
}

assert.True(t, metadata.ShouldRefresh())
})
}

func TestMetadata_TokenLifetime(t *testing.T) {
Expand Down

0 comments on commit f2def8d

Please sign in to comment.