chore(oauth): retire the * wildcard
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
Part of #60327 — implements RFC #1103.
Phase 4 — strictly ordered internally. Lands last.
Scope (in order)
- Roll the PostHog Code CLI forward to omit
scope=instead of sending["*"](overlaps I13's roll-forward). - Data-migrate existing
OAuthAccessTokenrows whose scope is*onto the resolved explicit set, so they keep working — must precede I2'sget_original_scopesoverride or that override must be*-aware (a*token intersected with the ceiling yields{}and gets rejected). These are the 7-day-TTL DCR/CIMD sessions. - Only then remove the
*short-circuit inAPIScopePermission(posthog/permissions.py:507) and rejectscope=*at/authorize. Also handle*indowngrade_scopes_to_read_only(posthog/scopes.py) and the SCOPES dict.
Validation
posthog/test/test_permissions.pywildcard cases; migration test that*rows convert;/authorize?scope=*→invalid_scope.
Gate: seed the PostHog Code desktop app (precedes step 1)
The PostHog Code desktop OAuth app (US + EU, the ARRAY_APP_CLIENT_ID_US/EU clients, a.k.a. POSTHOG_CODE_US/EU_APP_ID in the gateway config) authorizes with scope=["*"] and has an empty OAuthApplication.scopes ceiling, so it rides the * grandfather. Retiring * drops its llm_gateway:read (privileged, never in the unprivileged default) and the gateway rejects it once #60340 lands. Seed its ceiling first.
- Seed set:
UNPRIVILEGED_SCOPES ∪ {llm_gateway:read}, US + EU. Not a minimal list: the desktop agent proxies the user token to the PostHog MCP server, whose tools span the full unprivileged surface, so any subset would403some MCP tool. Excludellm_gateway:writeand hidden scopes. - Different from the Wizard gate (#60341): Wizard sends an explicit list, so a superset ceiling accepts its old client. PostHog Code sends
*, which a non-empty ceiling rejects at/authorize(OAuthValidator.validate_scopes). So seeding rejects old-CLI fresh logins until the CLI rolls forward; seed immediately before step 1, not as a free-standing prerequisite. Existing tokens refresh fine (the*passthrough inget_original_scopes) until the CLI bumpsOAUTH_SCOPE_VERSION. - Ordering: seed (US + EU) → step 1 (CLI roll-forward) → #60340 → step 2 (
*-token migration) → step 3 (rejectscope=*). Step 2 must run after the seed, or*resolves to the unprivileged default and migrated tokens losellm_gateway:read. - Not affected: the server-side direct-mint path (
create_oauth_access_token_for_user) already carriesllm_gateway:readand ignores the ceiling, so sandbox/background agents survive*retirement.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace the OAuth flow through posthog/permissions.py:507 and posthog/scopes.py, then inspect the wildcard cases in posthog/test/test_permissions.py. Verify the desktop app ceiling seed, CLI roll-forward, token migration, and /authorize?scope=* behavior in the stated order. Done means wildcard rows migrate safely, privileged scope access is preserved, and the endpoint returns invalid_scope.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, authentication, authorization, database
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100