Skip to content

feat: add repository scoped tokens - #1549

Open
brendan-kellam wants to merge 5 commits into
mainfrom
sou-1870-scoped-access-tokens
Open

feat: add repository scoped tokens#1549
brendan-kellam wants to merge 5 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellam brendan-kellam commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add one-hour opaque access tokens bound to explicit repository IDs and their creating user
  • authenticate sbst_ bearer tokens and intersect their repository scope with current user permissions across Prisma and search
  • expose API-key-only mint and revoke endpoints in the public API and documentation

Validation

  • yarn workspace @sourcebot/web test src/ee/features/scopedAccessTokens/api.test.ts src/middleware/withAuth.test.ts src/prisma.test.ts src/features/search/searchApi.test.ts --run (91 tests)
  • ESLint on all changed web TypeScript files
  • OpenAPI generation and JSON validation
  • live mint → list repositories → in-scope search → out-of-scope search → revoke flow

Linear: SOU-1870


Note

High Risk
New authentication path and repository scoping touch auth middleware, Prisma query filtering, and search—security-sensitive surfaces where mis-scoping could leak repository access.

Overview
Adds one-hour opaque bearer tokens (sbst_) limited to explicit repository IDs, mintable and revocable only with a Sourcebot API key under the new scoped-access-tokens entitlement.

Mint/revoke: POST /api/ee/scoped_access_token validates repoIds against the API-key owner’s org repos, stores hashed secrets with repo join rows, and returns the plaintext token once; DELETE /api/ee/scoped_access_token/{id} removes tokens owned by that user in the org.

Auth: Bearer sbst_ tokens resolve to the creating user and an AuthPrincipal carrying repositoryIds; withAuth supports requiredAuthSource: 'api_key' so scoped tokens cannot mint or revoke. The user-scoped Prisma extension intersects token repo IDs with permission-sync filters when enabled.

Search: Blocking and streaming search always constrain Zoekt to repos visible through the scoped client for scoped tokens (including empty scope), instead of treating them as unrestricted like API keys when permission syncing is off.

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

Summary by CodeRabbit

  • New Features
    • Added repository-scoped access tokens that expire after one hour.
    • Added APIs to create and revoke scoped tokens, with entitlement and ownership checks.
    • Scoped tokens restrict standard and streaming searches to authorized repositories.
    • Tokens are disclosed only once when created and support bearer-token authentication.
  • Documentation
    • Added API reference and authentication guidance for creating, using, and revoking scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlify Bot commented Aug 6, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
sourcebot 🟢 Ready View Preview Aug 6, 2026, 5:20 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Scoped access tokens now support hashed storage, one-hour expiration, API-key-only creation and revocation, bearer authentication, repository-scoped search, Prisma filtering, and public API documentation.

Changes

Scoped access tokens

Layer / File(s) Summary
Token storage and generation
packages/db/prisma/..., packages/shared/src/...
Adds scoped-token tables, repository associations, indexes, cascading relations, a token prefix, and hashed token generation.
Authentication and repository enforcement
packages/web/src/middleware/withAuth.ts, packages/web/src/prisma.ts, packages/web/src/middleware/withAuth.test.ts, packages/web/src/prisma.test.ts
Adds typed authentication principals, scoped bearer-token validation, source restrictions, organization checks, repository-scoped Prisma clients, intersected repository filters, and test coverage.
Token creation and revocation
packages/web/src/ee/features/scopedAccessTokens/*, packages/web/src/app/api/(server)/ee/scoped_access_token/*, packages/web/src/lib/errorCodes.ts
Adds validated token creation and ownership-scoped revocation APIs with service error handling and tests.
Search repository scopes
packages/web/src/features/search/searchApi.ts, packages/web/src/features/search/zoektSearcher.ts, packages/web/src/features/search/searchApi.test.ts
Passes explicit repository scopes to blocking and streaming search and applies Zoekt repository filters when required.
Public API contracts and documentation
packages/web/src/openapi/*, docs/api-reference/*, docs/docs.json, docs/docs/api-reference/authentication.mdx, CHANGELOG.md
Documents scoped-token schemas, endpoints, authentication rules, expiration, repository restrictions, and API navigation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding repository-scoped access tokens.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sou-1870-scoped-access-tokens

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread packages/web/src/middleware/withAuth.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/web/src/middleware/withAuth.test.ts (1)

304-312: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a negative test for a scoped token presented with the DPoP scheme.

getAuthenticatedUser rejects non-Bearer schemes at line 319 of packages/web/src/middleware/withAuth.ts, before the scoped-token branch at line 323. No test pins that ordering. A later refactor that moves the scoped-token branch above the scheme check would let a scoped token authenticate over DPoP without proof verification.

Add a test that sends the token with the DPoP scheme and asserts undefined.

🧪 Proposed test
         test('should return undefined for a token without a secret', async () => {
             setMockHeaders(new Headers({ 'Authorization': 'Bearer sbst_' }));
 
             const result = await getAuthenticatedUser();
 
             expect(result).toBeUndefined();
             expect(prisma.scopedAccessToken.findUnique).not.toHaveBeenCalled();
             expect(prisma.apiKey.findUnique).not.toHaveBeenCalled();
         });
+
+        test('should return undefined when a scoped token is presented with the DPoP scheme', async () => {
+            setMockHeaders(new Headers({ 'Authorization': 'DPoP sbst_scopedtoken' }));
+
+            const result = await getAuthenticatedUser();
+
+            expect(result).toBeUndefined();
+            expect(prisma.scopedAccessToken.findUnique).not.toHaveBeenCalled();
+        });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/middleware/withAuth.test.ts` around lines 304 - 312, Add a
negative test alongside the existing scoped-token tests that passes a scoped
token using the DPoP authorization scheme, calls getAuthenticatedUser, and
asserts it returns undefined. Also verify scoped-token and API-key lookups are
not called, preserving rejection before the scoped-token branch.
packages/web/src/ee/features/scopedAccessTokens/api.ts (1)

11-13: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider bounding the repos array length.

repos accepts an unbounded array. Each entry expands the IN list of the repo.findMany query and the nested create list. Add a .max(...) bound to keep the request cost predictable.

♻️ Proposed bound
 export const createScopedAccessTokenRequestSchema = z.object({
-    repos: z.array(z.string().min(1)).min(1),
+    repos: z.array(z.string().min(1)).min(1).max(100),
 }).strict();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/ee/features/scopedAccessTokens/api.ts` around lines 11 - 13,
Add a finite .max(...) constraint to the repos array in
createScopedAccessTokenRequestSchema, preserving the existing non-empty string
validation and strict object behavior. Use the project’s established maximum
collection-size constant if one exists; otherwise choose an appropriate bound
that keeps the downstream repo.findMany query and nested create list
predictable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/openapi/publicApiDocument.ts`:
- Around line 432-491: Document the EE license requirement for scoped access
tokens: in packages/web/src/openapi/publicApiDocument.ts lines 432-491, add the
x-mint metadata using EE_LICENSE_KEY_NOTE to both createScopedAccessToken and
revokeScopedAccessToken registrations, then regenerate the OpenAPI spec; in
docs/docs/api-reference/authentication.mdx lines 36-38, add a Note at the
beginning of the scoped access token section linking to
/docs/activating-a-subscription.

---

Nitpick comments:
In `@packages/web/src/ee/features/scopedAccessTokens/api.ts`:
- Around line 11-13: Add a finite .max(...) constraint to the repos array in
createScopedAccessTokenRequestSchema, preserving the existing non-empty string
validation and strict object behavior. Use the project’s established maximum
collection-size constant if one exists; otherwise choose an appropriate bound
that keeps the downstream repo.findMany query and nested create list
predictable.

In `@packages/web/src/middleware/withAuth.test.ts`:
- Around line 304-312: Add a negative test alongside the existing scoped-token
tests that passes a scoped token using the DPoP authorization scheme, calls
getAuthenticatedUser, and asserts it returns undefined. Also verify scoped-token
and API-key lookups are not called, preserving rejection before the scoped-token
branch.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a200573c-d681-4fde-ad8f-91059c43107e

📥 Commits

Reviewing files that changed from the base of the PR and between 472692a and 2d6c321.

📒 Files selected for processing (23)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
  • docs/docs.json
  • docs/docs/api-reference/authentication.mdx
  • packages/db/prisma/migrations/20260806033656_add_scoped_access_tokens/migration.sql
  • packages/db/prisma/schema.prisma
  • packages/shared/src/constants.ts
  • packages/shared/src/crypto.ts
  • packages/shared/src/index.server.ts
  • packages/web/src/app/api/(server)/ee/scoped_access_token/[id]/route.ts
  • packages/web/src/app/api/(server)/ee/scoped_access_token/route.ts
  • packages/web/src/ee/features/scopedAccessTokens/api.test.ts
  • packages/web/src/ee/features/scopedAccessTokens/api.ts
  • packages/web/src/features/search/searchApi.test.ts
  • packages/web/src/features/search/searchApi.ts
  • packages/web/src/features/search/zoektSearcher.ts
  • packages/web/src/lib/errorCodes.ts
  • packages/web/src/middleware/withAuth.test.ts
  • packages/web/src/middleware/withAuth.ts
  • packages/web/src/openapi/publicApiDocument.ts
  • packages/web/src/openapi/publicApiSchemas.ts
  • packages/web/src/prisma.test.ts
  • packages/web/src/prisma.ts

Comment thread packages/web/src/openapi/publicApiDocument.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 1 potential issue.

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

Comment thread packages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
Contributor Author

Companion Lighthouse entitlement registry PR: https://github.com/sourcebot-dev/lighthouse/pull/52. Deploy the Lighthouse change first so signed license assertions can grant scoped-access-tokens before this PR begins enforcing it.

@brendan-kellam brendan-kellam changed the title SOU-1870: Add repository-scoped access tokens feat: add repository scoped tokens Aug 6, 2026
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.

1 participant