Skip to content
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

Mark tokens as expired instead of deleting them #307

Closed
wants to merge 7 commits into from

Conversation

S7evinK
Copy link
Collaborator

@S7evinK S7evinK commented Sep 18, 2023

Fixes #305

This PR changes the following:

  • adds a expired column which defaults to false
  • Sets the expired flag when we receive a 401 from upstream
  • when setting up a connection and we know about the hash, but it is expired, returns 401 to the client
  • starts a timer to remove expired tokens


var deleteExpiredTokens func()
deleteExpiredTokens = func() {
deleted, err := t.deleteExpiredTokensAfter(30 * 24 * time.Hour)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30 days might be a bit much for OIDC, so maybe a bit lower?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I used 30 days in #242 for a different (but related) purpose.

@S7evinK
Copy link
Collaborator Author

S7evinK commented Sep 18, 2023

With the failing test, I'm wondering the same as @DMRobertson here #302 (comment)
The new test added in #302 is happy here, but the "old" one is failing.

Copy link
Member

@kegsay kegsay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs some tweaks but otherwise this is great.

logger.Trace().Int64("deleted", deleted).Msg("deleted expired tokens")
time.AfterFunc(time.Hour, deleteExpiredTokens)
}
time.AfterFunc(time.Hour, deleteExpiredTokens)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't tie this to the New... constructor. This is going to be such a faff to work with in tests otherwise.

deleted, err := t.deleteExpiredTokensAfter(30 * 24 * time.Hour)
if err != nil {
logger.Warn().Err(err).Msg("failed to delete expired tokens")
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't make sense to then never delete ever again :S surely we should retry in 1h?

@@ -258,3 +275,38 @@ func (t *TokensTable) Delete(accessTokenHash string) error {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (t *TokensTable) Delete(accessTokenHash string) error { is unused now then? Remove it please.

StatusCode: http.StatusUnauthorized,
ErrCode: "M_UNKNOWN_TOKEN",
// not exactly /whoami, but would be if we didn't keep the expired state
Err: fmt.Errorf("/whoami returned HTTP 401"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't lie else this will be confusing when we see this in bug reports. Say "token marked as expired" instead.

return nil
})
t.Log("We should be able to fetch this token without error.")
_, err = tokens.Token(accessToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify the token is not expired.

}

t.Log("Deletes expired tokens")
deleted, err := tokens.deleteExpiredTokensAfter(time.Nanosecond)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd check before this that tokens.deleteExpiredTokensAfter(time.Hour) has deleted == 0 i.e it didn't delete anything.

@kegsay
Copy link
Member

kegsay commented Sep 18, 2023

With the failing test, I'm wondering the same as @DMRobertson here #302 (comment) The new test added in #302 is happy here, but the "old" one is failing.

This isn't quite right. The failing test is TestPollersCanBeResumedAfterExpiry. The tests referred to in #302 are TestPollerExpiryEnsurePollingRace and TestPollerExpiryEnsurePollingRaceDoesntWedge.

The failing test is 401ing alice's token because it is old as per comments:

// Inject an old token from Alice and a new token from Bob into the DB.

It then calls h.callbacks.OnExpiredToken(context.Background(), hashToken(p.accessToken), p.userID, p.deviceID) due to the test calling:

	t.Log("Manually trigger a poller cleanup.")
	v3.h2.ExpireOldPollers()

So the token for alice will be marked as expired in the DB in this PR, hence the 401. The failing section of test is:

	t.Log("Alice makes a new sliding sync request")
	res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
		Extensions: extensions.Request{
			AccountData: &extensions.AccountDataRequest{
				Core: extensions.Core{
					Enabled: &boolTrue,
				},
			},
		},
	})

	t.Log("Alice's poller should have been polled.")
	v2.waitUntilEmpty(t, aliceToken)

Which makes sense as the expired aliceToken won't hit /whoami or anything anymore, it'll just immediately 401. The test needs to be updated to use a new token for Alice.

@S7evinK
Copy link
Collaborator Author

S7evinK commented Apr 12, 2024

@kegsay is it still worth taking a look at this or can we close this?

@kegsay
Copy link
Member

kegsay commented Apr 26, 2024

I think we can close this for now. We may want to revisit this later on.

@kegsay kegsay closed this Apr 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mark expired tokens in the DB rather than DELETEing them
3 participants