Skip to content

Add project-scoped OAuth authorization - #138

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

Add project-scoped OAuth authorization#138
masnwilliams wants to merge 8 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
  • verify Clerk organization membership during exchange and refresh 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 (112 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, authorization boundaries, and token/refresh persistence; incorrect scope handling could over- or under-grant API access, and deployment depends on kernel#3165 for structured contexts.

Overview
Adds organization-wide vs single-project OAuth consent, with project-scoped tokens locked to one Kernel project and organization-wide grants unchanged for existing clients.

The org picker becomes a two-step flow: pick an org, then choose entire organization or a searchable active project (via new GET /oauth/projects). Selection is sent to /authorize as access_scope / project_id, validated against the user’s active org and (for projects) the Kernel API.

Authorization context replaces Redis org-only strings with structured scope (org + optional project_id), stored on PKCE-bound requests, JWTs, and refresh tokens. /authorize enforces S256 PKCE for shared clients and all project grants; /token resolves scope only from server-side storage, strips client-supplied scope params before Clerk, checks JWT subject and org membership, returns org_id / access_scope / project_id, and blocks refresh-time scope escalation. Legacy org_* Redis values still decode as organization-wide.

Routes are refactored for testable dependencies; broad Bun tests cover authorize, token, register, projects, and context helpers. README documents the new consent behavior.

Reviewed by Cursor Bugbot for commit 320ddc3. 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 5, 2026 8:19pm
mcp (staging) Ready Ready Preview Aug 5, 2026 8:19pm

Comment thread src/app/authorize/route.ts
Comment thread src/app/authorize/route.ts

@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 4 potential issues.

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 b10cb02. Configure here.

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.

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