Skip to content

fix(api): scope workspace API tokens to their workspace#369

Open
cavidelizade wants to merge 1 commit into
Devlaner:mainfrom
cavidelizade:fix/token-workspace-scope
Open

fix(api): scope workspace API tokens to their workspace#369
cavidelizade wants to merge 1 commit into
Devlaner:mainfrom
cavidelizade:fix/token-workspace-scope

Conversation

@cavidelizade

Copy link
Copy Markdown
Contributor

Summary

A "workspace service token" set a WorkspaceID on its row, but UserFromAPIToken ignored it and returned the full user, so a token handed out as scoped to one workspace could reach every other workspace the owner belonged to, /users/me, and even mint more personal tokens. The scope column was decorative.

Linked issues

Closes #325

Type of change

  • Bug fix (fix:)

Surface

  • API (apps/api/)

What changed

RequireAuth now enforces the scope: when a bearer token carries a WorkspaceID, the request must target that workspace (its :slug resolves to the token's workspace), otherwise it returns 403. Personal tokens (no WorkspaceID) and cookie sessions are untouched, so the change only affects workspace-scoped tokens.

  • auth.Service.UserFromAPITokenScoped returns the token record alongside the user.
  • auth.Service.SlugMatchesWorkspace resolves the route :slug and compares it to the token's workspace.
  • Wired the workspace store into the auth service in the router.

Why this approach

Doing the check in RequireAuth, gated on tok.WorkspaceID != nil, keeps the common paths (sessions, personal tokens) on exactly their existing code path, so a mistake here can only affect the rarely-used workspace-token feature.

Test plan

  • go build, go vet, go test ./... green, including TestWorkspaceTokenScope: a workspace token reaches its own workspace, is denied on another workspace and on /users/me, and a personal token still works everywhere.

AI assistance

  • AI tools were used, tool(s): Claude Code (Opus 4.8), commits carry a Co-Authored-By: trailer

A "workspace service token" set a WorkspaceID on the row, but UserFromAPIToken
ignored it and returned the full user, so a token handed out as scoped to one
workspace could reach every other workspace the owner belonged to, /users/me,
and even mint more personal tokens.

RequireAuth now enforces the scope: when a bearer token carries a WorkspaceID,
the request must target that workspace (its :slug resolves to the token's
workspace), otherwise it's 403. Personal tokens (no WorkspaceID) and cookie
sessions are unchanged, so the blast radius is limited to workspace tokens.

Covered by a test: a workspace token reaches its own workspace, is denied on
another workspace and on /users/me, while a personal token still works
everywhere.

Closes Devlaner#325

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cavidelizade
cavidelizade requested a review from a team as a code owner July 16, 2026 21:25
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@cavidelizade, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8103ae18-231a-4c38-98bc-d4ab076ef0d5

📥 Commits

Reviewing files that changed from the base of the PR and between 61bc903 and 242b903.

📒 Files selected for processing (4)
  • apps/api/internal/auth/service.go
  • apps/api/internal/handler/token_scope_test.go
  • apps/api/internal/middleware/auth.go
  • apps/api/internal/router/router.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Workspace service tokens are not scoped to their workspace

1 participant