Periodic cleanup of expired OAuth2 tokens#11530
Draft
pieterbeulque wants to merge 1 commit into
Draft
Conversation
The MCP "create product with AI" flow uses a custom web grant that issues short-lived (1h) OAuth2 access tokens but no refresh token, so the oauth2_tokens table accumulated rows that were never deleted. Add a daily cron actor that prunes tokens whose access has expired and which either have no refresh token or whose refresh token has been revoked, so third-party OAuth apps with active refresh paths are preserved. Tokens issued to the legacy Polar app client are also excluded to keep the existing expired-token workaround intact.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
OpenAPI ChangesNo changes detected in the OpenAPI schema. |
Contributor
|
Preview Environment |
frankie567
approved these changes
May 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(sorry, this shouldn't've created a PR, but wanted to ask if this is something we should/could/need to do?)
Summary
The MCP "create product with AI" flow exchanges the user's session for a 1h OAuth2 access token via the custom
webgrant on every cache miss (cookie cached forexpires_inseconds). The grant doesn't issue a refresh token, so each issuance is a brand-new row inoauth2_tokens, and there's no job that ever deletes them —OAuth2TokenService.get_by_access_tokenonly checksis_expired()at lookup time, leaving expired rows behind forever.This adds a daily cron actor that prunes safely-deletable expired tokens, mirroring the existing pattern from
customer_session.delete_expiredandauth.delete_expired.What gets deleted
A row is deleted only if all of the following hold:
issued_at + expires_in < now()(expires_atis a Python property, not a column, so the SQL has to expand it).refresh_token IS NULL OR refresh_token_revoked_at != 0. This preserves third-party OAuth apps that still have a valid refresh token they could use to mint a new access token.client_idis not the legacy Polar app client (APP_CLIENT_ID). The service intentionally accepts expired tokens from that client as a temporary workaround for an unfixed mobile auth flow — deleting those rows would break the workaround.Files
server/polar/oauth2/repository.py(new) —OAuth2TokenRepository.delete_expired(*, exclude_client_ids).server/polar/oauth2/service/oauth2_token.py—OAuth2TokenService.delete_expiredwires theAPP_CLIENT_IDexclusion.server/polar/oauth2/tasks.py(new) —oauth2_token.delete_expiredcron actor (hour=0, minute=0,TaskPriority.LOW,max_retries=0).server/polar/tasks.py— registers the new tasks module.server/tests/oauth2/service/test_oauth2_token.py—TestDeleteExpiredcovering the four cases above.Test plan
TestDeleteExpired(local execution blocked by a pre-existing Python 3.14rc2 / pydantic-ai import failure in this sandbox; ruff passes on all touched files).oauth2_tokensrow count stops growing for client_id values without refresh tokens.https://claude.ai/code/session_01LJttmtBWXZwktjxLRJnHpt
Generated by Claude Code