PostHog / PostHog/posthog

chore(oauth): retire the * wildcard

Open
#60,342 3 comments 0 reactions 0 assignees View on GitHub

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)
  1. Roll the PostHog Code CLI forward to omit scope= instead of sending ["*"] (overlaps I13's roll-forward).
  2. Data-migrate existing OAuthAccessToken rows whose scope is * onto the resolved explicit set, so they keep working — must precede I2's get_original_scopes override 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.
  3. Only then remove the * short-circuit in APIScopePermission (posthog/permissions.py:507) and reject scope=* at /authorize. Also handle * in downgrade_scopes_to_read_only (posthog/scopes.py) and the SCOPES dict.
Validation
  • posthog/test/test_permissions.py wildcard 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 would 403 some MCP tool. Exclude llm_gateway:write and 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 in get_original_scopes) until the CLI bumps OAUTH_SCOPE_VERSION.
  • Ordering: seed (US + EU) → step 1 (CLI roll-forward) → #60340 → step 2 (*-token migration) → step 3 (reject scope=*). Step 2 must run after the seed, or * resolves to the unprivileged default and migrated tokens lose llm_gateway:read.
  • Not affected: the server-side direct-mint path (create_oauth_access_token_for_user) already carries llm_gateway:read and ignores the ceiling, so sandbox/background agents survive * retirement.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.