Skip to content

Add project-scoped OAuth authorization - #138

Open
masnwilliams wants to merge 9 commits into
mainfrom
hypeship/project-scoped-oauth
Open

Add project-scoped OAuth authorization#138
masnwilliams wants to merge 9 commits into
mainfrom
hypeship/project-scoped-oauth

Conversation

@masnwilliams

@masnwilliams masnwilliams commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

summary

  • add an explicit authorization choice between organization-wide access and one active project
  • bind authorization context to PKCE requests, access tokens, and rotated refresh tokens with atomic Redis persistence
  • verify Clerk organization membership before refresh rotation and during code exchange, and reject client-supplied scope escalation
  • keep legacy org-only access and refresh mappings organization-wide
  • add route-level coverage for registration, authorization, token exchange, refresh, project validation, concurrency boundaries, and negative cases

tests

  • bun test (170 passing)
  • targeted Prettier checks for changed files
  • bun run build with test build-time configuration

dependency

Requires kernel/kernel#3165 to be deployed before this change writes structured OAuth contexts.


Note

High Risk
Changes authentication, token issuance, refresh rotation, and authorization boundaries; incorrect context handling could widen or break access for existing clients.

Overview
Adds organization-wide vs single-project OAuth grants. Users pick scope on /select-org (with searchable project list when the client uses PKCE S256); /authorize validates membership, enforces PKCE for shared clients and project scope, verifies active projects via the Kernel API, and stores a versioned authorization context in Redis (PKCE-bound request keys or legacy client-id mapping).

The token endpoint resolves scope only from stored context—client-supplied org_id / access_scope / project_id are stripped before Clerk—and returns org_id, access_scope, and optional project_id. JWT and refresh mappings persist the full context; refresh keeps project scope and rejects escalation. Legacy Redis values that are plain org_* IDs still decode as organization-wide access.

New GET /oauth/projects supports the picker; README notes project-scoped tokens cannot switch projects.

Reviewed by Cursor Bugbot for commit 478cd7b. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mcp Ready Ready Preview Aug 11, 2026 12:37am
mcp (staging) Ready Ready Preview Aug 11, 2026 12:37am

Comment thread src/app/authorize/route.ts
Comment thread src/app/authorize/route.ts
Comment thread src/app/select-org/page.tsx
Comment thread src/app/select-org/page.tsx
Comment thread src/app/select-org/page.tsx
Comment thread src/lib/oauth-projects.ts
@hiroTamada

Copy link
Copy Markdown

i dont know too much about ts, so take it or leave it:

  • commit the JWT mapping, refresh-token rotation, and request-context deletion in one Redis transaction. the current sequential writes can leave partial state after Clerk has consumed the code or rotated the token. refresh-context lookup should also be read-only until the exchange succeeds.
  • split select-org/page.tsx before adding more behavior. it grew from 255 to 545 lines and now combines org activation, project fetching, pagination, debounce/race handling, scrolling, and selection state. a useOAuthProjects reducer plus a focused scope picker would make this substantially easier to reason about. the selected scope should also be a discriminated union rather than project:${id} string parsing.
  • use the existing @onkernel/sdk project client instead of maintaining another implementation of project URLs, authentication headers, response types, and pagination semantics in oauth-projects.ts.
  • validate authorization-context JSON rather than casting it. numeric clerk_org_id and project_id values currently pass parseAuthorizationContext, so malformed data can violate the declared type and propagate through Redis.

the behavior and test coverage are headed in the right direction, but these are authorization boundaries and long-lived flows. i’d rather simplify and harden the structure now than preserve the added incidental complexity.

Comment thread src/app/token/route.ts
Comment thread src/app/token/route.ts
Comment thread src/app/token/route.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1d265ac. Configure here.

Comment thread src/app/select-org/page.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants