feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: add repository scoped tokens - #1549

Merged
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens
Aug 13, 2026
Merged

feat: add repository scoped tokens#1549
brendan-kellam merged 6 commits into
mainfrom
sou-1870-scoped-access-tokens

Conversation

@brendan-kellam

@brendan-kellambrendan-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 tokens with entitlement, ownership, and repository-access 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 scoped access tokens.
  • Bug Fixes
    • Improved repository permission enforcement across searches and token validation.

@mintlify

mintlifyBot commented Aug 6, 2026

Copy link
Copy Markdown

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

ProjectStatusPreviewUpdated (UTC)
sourcebot🟢 ReadyView PreviewAug 6, 2026, 5:20 AM

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

@github-actions

This comment has been minimized.

@coderabbitai

coderabbitaiBot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b5da7f24-e257-4478-a68d-988016aae886

📥 Commits

Reviewing files that changed from the base of the PR and between c9b3cbd and e16eee2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json

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, entitlement support, 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

Mergeability Score:⚪ Minimal · up to e16ee

The repository-scoped token behavior is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
participant Client
participant Auth
participant Prisma
participant Search
participant Zoekt
Client->>Auth: Send scoped bearer token
Auth->>Prisma: Validate token and load repository scope
Auth->>Search: Provide authenticated principal
Search->>Zoekt: Submit repository-scoped search request
Zoekt-->>Client: Return search results
Loading

Possibly related PRs

Suggested reviewers:jsourcebot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly summarizes the main change: adding repository-scoped access tokens.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 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 threadpackages/web/src/middleware/withAuth.ts

@coderabbitaicoderabbitaiBot 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 threadpackages/web/src/openapi/publicApiDocument.ts

@cursorcursorBot 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 threadpackages/web/src/middleware/withAuth.ts
@brendan-kellam

Copy link
Copy Markdown
ContributorAuthor

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-kellambrendan-kellam changed the title SOU-1870: Add repository-scoped access tokensfeat: add repository scoped tokensAug 6, 2026
@brendan-kellam
brendan-kellam merged commit 3a4447b into mainAug 13, 2026
15 checks passed
@github-actionsgithub-actionsBot mentioned this pull request Aug 13, 2026
Sign up for freeto 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

@brendan-kellam@jsourcebot