diff --git a/pkg/handler/handler_test.go b/pkg/handler/handler_test.go index 7949c8e..ad7627b 100644 --- a/pkg/handler/handler_test.go +++ b/pkg/handler/handler_test.go @@ -174,29 +174,29 @@ func TestHandler_SessionInfo(t *testing.T) { assert.NoError(t, err) allowedSkew := 5 * time.Second - assert.WithinDuration(t, time.Now(), data.SessionCreatedAt, allowedSkew) - assert.WithinDuration(t, time.Now().Add(cfg.Session.MaxLifetime), data.SessionEndsAt, allowedSkew) - assert.WithinDuration(t, time.Now().Add(idp.ProviderHandler.TokenDuration), data.TokensExpireAt, allowedSkew) - assert.WithinDuration(t, time.Now(), data.TokensRefreshedAt, allowedSkew) + assert.WithinDuration(t, time.Now(), data.Session.CreatedAt, allowedSkew) + assert.WithinDuration(t, time.Now().Add(cfg.Session.MaxLifetime), data.Session.EndsAt, allowedSkew) + assert.WithinDuration(t, time.Now().Add(idp.ProviderHandler.TokenDuration), data.Tokens.ExpireAt, allowedSkew) + assert.WithinDuration(t, time.Now(), data.Tokens.RefreshedAt, allowedSkew) - sessionEndDuration := time.Duration(data.SessionEndsInSeconds) * time.Second + sessionEndDuration := time.Duration(data.Session.EndsInSeconds) * time.Second // 1 second < time until session ends <= configured max session lifetime assert.LessOrEqual(t, sessionEndDuration, cfg.Session.MaxLifetime) assert.Greater(t, sessionEndDuration, time.Second) - tokenExpiryDuration := time.Duration(data.TokensExpireInSeconds) * time.Second + tokenExpiryDuration := time.Duration(data.Tokens.ExpireInSeconds) * time.Second // 1 second < time until token expires <= max duration for tokens from IDP assert.LessOrEqual(t, tokenExpiryDuration, idp.ProviderHandler.TokenDuration) assert.Greater(t, tokenExpiryDuration, time.Second) // 1 second < next token refresh <= seconds until token expires - assert.LessOrEqual(t, data.TokensNextRefreshInSeconds, data.TokensExpireInSeconds) - assert.Greater(t, data.TokensNextRefreshInSeconds, int64(1)) + assert.LessOrEqual(t, data.Tokens.NextAutoRefreshInSeconds, data.Tokens.ExpireInSeconds) + assert.Greater(t, data.Tokens.NextAutoRefreshInSeconds, int64(1)) - assert.True(t, data.TokensRefreshCooldown) + assert.True(t, data.Tokens.RefreshCooldown) // 1 second < refresh cooldown <= minimum refresh interval - assert.LessOrEqual(t, data.TokensRefreshCooldownSeconds, session.RefreshMinInterval) - assert.Greater(t, data.TokensRefreshCooldownSeconds, int64(1)) + assert.LessOrEqual(t, data.Tokens.RefreshCooldownSeconds, session.RefreshMinInterval) + assert.Greater(t, data.Tokens.RefreshCooldownSeconds, int64(1)) } func TestHandler_SessionInfo_Disabled(t *testing.T) { @@ -249,7 +249,7 @@ func TestHandler_SessionRefresh(t *testing.T) { err = json.Unmarshal([]byte(resp.Body), &temp) assert.NoError(t, err) - if !temp.TokensRefreshCooldown { + if !temp.Tokens.RefreshCooldown { return } } @@ -264,35 +264,35 @@ func TestHandler_SessionRefresh(t *testing.T) { assert.NoError(t, err) // session create and end times should be unchanged - assert.WithinDuration(t, data.SessionCreatedAt, refreshedData.SessionCreatedAt, 0) - assert.WithinDuration(t, data.SessionEndsAt, refreshedData.SessionEndsAt, 0) + assert.WithinDuration(t, data.Session.CreatedAt, refreshedData.Session.CreatedAt, 0) + assert.WithinDuration(t, data.Session.EndsAt, refreshedData.Session.EndsAt, 0) // token expiration and refresh times should be later than before - assert.True(t, refreshedData.TokensExpireAt.After(data.TokensExpireAt)) - assert.True(t, refreshedData.TokensRefreshedAt.After(data.TokensRefreshedAt)) + assert.True(t, refreshedData.Tokens.ExpireAt.After(data.Tokens.ExpireAt)) + assert.True(t, refreshedData.Tokens.RefreshedAt.After(data.Tokens.RefreshedAt)) allowedSkew := 5 * time.Second - assert.WithinDuration(t, time.Now().Add(idp.ProviderHandler.TokenDuration), refreshedData.TokensExpireAt, allowedSkew) - assert.WithinDuration(t, time.Now(), refreshedData.TokensRefreshedAt, allowedSkew) + assert.WithinDuration(t, time.Now().Add(idp.ProviderHandler.TokenDuration), refreshedData.Tokens.ExpireAt, allowedSkew) + assert.WithinDuration(t, time.Now(), refreshedData.Tokens.RefreshedAt, allowedSkew) - sessionEndDuration := time.Duration(refreshedData.SessionEndsInSeconds) * time.Second + sessionEndDuration := time.Duration(refreshedData.Session.EndsInSeconds) * time.Second // 1 second < time until session ends <= configured max session lifetime assert.LessOrEqual(t, sessionEndDuration, cfg.Session.MaxLifetime) assert.Greater(t, sessionEndDuration, time.Second) - tokenExpiryDuration := time.Duration(refreshedData.TokensExpireInSeconds) * time.Second + tokenExpiryDuration := time.Duration(refreshedData.Tokens.ExpireInSeconds) * time.Second // 1 second < time until token expires <= max duration for tokens from IDP assert.LessOrEqual(t, tokenExpiryDuration, idp.ProviderHandler.TokenDuration) assert.Greater(t, tokenExpiryDuration, time.Second) // 1 second < next token refresh <= seconds until token expires - assert.LessOrEqual(t, refreshedData.TokensNextRefreshInSeconds, refreshedData.TokensExpireInSeconds) - assert.Greater(t, refreshedData.TokensNextRefreshInSeconds, int64(1)) + assert.LessOrEqual(t, refreshedData.Tokens.NextAutoRefreshInSeconds, refreshedData.Tokens.ExpireInSeconds) + assert.Greater(t, refreshedData.Tokens.NextAutoRefreshInSeconds, int64(1)) - assert.True(t, refreshedData.TokensRefreshCooldown) + assert.True(t, refreshedData.Tokens.RefreshCooldown) // 1 second < refresh cooldown <= minimum refresh interval - assert.LessOrEqual(t, refreshedData.TokensRefreshCooldownSeconds, session.RefreshMinInterval) - assert.Greater(t, refreshedData.TokensRefreshCooldownSeconds, int64(1)) + assert.LessOrEqual(t, refreshedData.Tokens.RefreshCooldownSeconds, session.RefreshMinInterval) + assert.Greater(t, refreshedData.Tokens.RefreshCooldownSeconds, int64(1)) } func TestHandler_SessionRefresh_Disabled(t *testing.T) { diff --git a/pkg/session/data.go b/pkg/session/data.go index f117238..4976b63 100644 --- a/pkg/session/data.go +++ b/pkg/session/data.go @@ -100,28 +100,40 @@ func (in *Data) HasRefreshToken() bool { } type Metadata struct { - // SessionCreatedAt is the time when the session was created. - SessionCreatedAt time.Time `json:"session_created_at"` - // SessionEndsAt is the time when the session will end, i.e. the absolute lifetime/time-to-live for the session. - SessionEndsAt time.Time `json:"session_ends_at"` - // TokensExpireAt is the time when the tokens within the session expires. - TokensExpireAt time.Time `json:"tokens_expire_at"` - // TokensRefreshedAt is the time when the tokens within the session was refreshed. - TokensRefreshedAt time.Time `json:"tokens_refreshed_at"` + Session MetadataSession `json:"session"` + Tokens MetadataTokens `json:"tokens"` +} + +type MetadataSession struct { + // CreatedAt is the time when the session was created. + CreatedAt time.Time `json:"created_at"` + // EndsAt is the time when the session will end, i.e. the absolute lifetime/time-to-live for the session. + EndsAt time.Time `json:"ends_at"` +} + +type MetadataTokens struct { + // ExpireAt is the time when the tokens will expire. + ExpireAt time.Time `json:"expire_at"` + // RefreshedAt is the time when the tokens were last refreshed. + RefreshedAt time.Time `json:"refreshed_at"` } func NewMetadata(expiresIn time.Duration, endsIn time.Duration) *Metadata { now := time.Now() return &Metadata{ - SessionCreatedAt: now, - SessionEndsAt: now.Add(endsIn), - TokensRefreshedAt: now, - TokensExpireAt: now.Add(expiresIn), + Session: MetadataSession{ + CreatedAt: now, + EndsAt: now.Add(endsIn), + }, + Tokens: MetadataTokens{ + ExpireAt: now.Add(expiresIn), + RefreshedAt: now, + }, } } func (in *Metadata) IsExpired() bool { - return time.Now().After(in.TokensExpireAt) + return time.Now().After(in.Tokens.ExpireAt) } func (in *Metadata) IsRefreshOnCooldown() bool { @@ -130,7 +142,7 @@ func (in *Metadata) IsRefreshOnCooldown() bool { func (in *Metadata) NextRefresh() time.Time { // subtract the leeway to ensure that we refresh before expiry - next := in.TokensExpireAt.Add(-RefreshLeeway) + next := in.Tokens.ExpireAt.Add(-RefreshLeeway) // try to refresh at the first opportunity if the next refresh is in the past if next.Before(time.Now()) { @@ -142,12 +154,12 @@ func (in *Metadata) NextRefresh() time.Time { func (in *Metadata) Refresh(nextExpirySeconds int64) { now := time.Now() - in.TokensRefreshedAt = now - in.TokensExpireAt = now.Add(time.Duration(nextExpirySeconds) * time.Second) + in.Tokens.RefreshedAt = now + in.Tokens.ExpireAt = now.Add(time.Duration(nextExpirySeconds) * time.Second) } func (in *Metadata) RefreshCooldown() time.Time { - refreshed := in.TokensRefreshedAt + refreshed := in.Tokens.RefreshedAt tokenLifetime := in.TokenLifetime() // if token lifetime is less than the minimum refresh interval * 2, we'll allow refreshes at the token half-life @@ -167,33 +179,47 @@ func (in *Metadata) ShouldRefresh() bool { } func (in *Metadata) TokenLifetime() time.Duration { - return in.TokensExpireAt.Sub(in.TokensRefreshedAt) + return in.Tokens.ExpireAt.Sub(in.Tokens.RefreshedAt) } func (in *Metadata) Verbose() MetadataVerbose { now := time.Now() - expireTime := in.TokensExpireAt - endTime := in.SessionEndsAt + expireTime := in.Tokens.ExpireAt + endTime := in.Session.EndsAt nextRefreshTime := in.NextRefresh() return MetadataVerbose{ - Metadata: *in, - SessionEndsInSeconds: toSeconds(endTime.Sub(now)), - TokensExpireInSeconds: toSeconds(expireTime.Sub(now)), - TokensNextRefreshInSeconds: toSeconds(nextRefreshTime.Sub(now)), - TokensRefreshCooldown: in.IsRefreshOnCooldown(), - TokensRefreshCooldownSeconds: toSeconds(in.RefreshCooldown().Sub(now)), + Session: MetadataSessionVerbose{ + MetadataSession: in.Session, + EndsInSeconds: toSeconds(endTime.Sub(now)), + }, + Tokens: MetadataTokensVerbose{ + MetadataTokens: in.Tokens, + ExpireInSeconds: toSeconds(expireTime.Sub(now)), + NextAutoRefreshInSeconds: toSeconds(nextRefreshTime.Sub(now)), + RefreshCooldown: in.IsRefreshOnCooldown(), + RefreshCooldownSeconds: toSeconds(in.RefreshCooldown().Sub(now)), + }, } } type MetadataVerbose struct { - Metadata - SessionEndsInSeconds int64 `json:"session_ends_in_seconds"` - TokensExpireInSeconds int64 `json:"tokens_expire_in_seconds"` - TokensNextRefreshInSeconds int64 `json:"tokens_next_refresh_in_seconds"` - TokensRefreshCooldown bool `json:"tokens_refresh_cooldown"` - TokensRefreshCooldownSeconds int64 `json:"tokens_refresh_cooldown_seconds"` + Session MetadataSessionVerbose `json:"session"` + Tokens MetadataTokensVerbose `json:"tokens"` +} + +type MetadataSessionVerbose struct { + MetadataSession + EndsInSeconds int64 `json:"ends_in_seconds"` +} + +type MetadataTokensVerbose struct { + MetadataTokens + ExpireInSeconds int64 `json:"expire_in_seconds"` + NextAutoRefreshInSeconds int64 `json:"next_auto_refresh_in_seconds"` + RefreshCooldown bool `json:"refresh_cooldown"` + RefreshCooldownSeconds int64 `json:"refresh_cooldown_seconds"` } func toSeconds(d time.Duration) int64 { diff --git a/pkg/session/data_test.go b/pkg/session/data_test.go index a1278cf..2262439 100644 --- a/pkg/session/data_test.go +++ b/pkg/session/data_test.go @@ -12,6 +12,14 @@ func TestData_HasRefreshToken(t *testing.T) { // TODO } +func TestMetadata_IsExpired(t *testing.T) { + // TODO +} + +func TestMetadata_IsRefreshOnCooldown(t *testing.T) { + // TODO +} + func TestMetadata_NextRefresh(t *testing.T) { // TODO } @@ -24,15 +32,11 @@ func TestMetadata_RefreshCooldown(t *testing.T) { // TODO } -func TestMetadata_RefreshOnCooldown(t *testing.T) { - // TODO -} - func TestMetadata_ShouldRefresh(t *testing.T) { // TODO } -func TestMetadata_TokenAge(t *testing.T) { +func TestMetadata_TokenLifetime(t *testing.T) { // TODO } diff --git a/pkg/session/store_test.go b/pkg/session/store_test.go index f9822a4..6af9bc8 100644 --- a/pkg/session/store_test.go +++ b/pkg/session/store_test.go @@ -21,10 +21,10 @@ func decryptedEqual(t *testing.T, expected, actual *session.Data) { assert.Equal(t, expected.IDTokenJwtID, actual.IDTokenJwtID) assert.Equal(t, expected.ExternalSessionID, actual.ExternalSessionID) - assert.WithinDuration(t, expected.Metadata.SessionCreatedAt, actual.Metadata.SessionCreatedAt, 0) - assert.WithinDuration(t, expected.Metadata.SessionEndsAt, actual.Metadata.SessionEndsAt, 0) - assert.WithinDuration(t, expected.Metadata.TokensExpireAt, actual.Metadata.TokensExpireAt, 0) - assert.WithinDuration(t, expected.Metadata.TokensRefreshedAt, actual.Metadata.TokensRefreshedAt, 0) + assert.WithinDuration(t, expected.Metadata.Session.CreatedAt, actual.Metadata.Session.CreatedAt, 0) + assert.WithinDuration(t, expected.Metadata.Session.EndsAt, actual.Metadata.Session.EndsAt, 0) + assert.WithinDuration(t, expected.Metadata.Tokens.ExpireAt, actual.Metadata.Tokens.ExpireAt, 0) + assert.WithinDuration(t, expected.Metadata.Tokens.RefreshedAt, actual.Metadata.Tokens.RefreshedAt, 0) } func makeCrypter(t *testing.T) crypto.Crypter {