Skip to content

fix(routerlicious): enforce scope checks on PATCH /root endpoint (#27…#27097

Merged
sonalideshpandemsft merged 2 commits intorelease/server/6.0from
fix-scopes-6.0-1
Apr 23, 2026
Merged

fix(routerlicious): enforce scope checks on PATCH /root endpoint (#27…#27097
sonalideshpandemsft merged 2 commits intorelease/server/6.0from
fix-scopes-6.0-1

Conversation

@sonalideshpandemsft
Copy link
Copy Markdown
Contributor

…073)

The PATCH /:tenantId/:id/root endpoint in Alfred's REST API called verifyToken() without passing the requiredScopes parameter. This meant any valid JWT — including tokens with only doc:read scope — could write arbitrary key-value data into a document's root map. The injected operations were permanently sequenced into the document's op log and replayed by all connected clients.

The fix threads a requiredScopes parameter through the verifyRequestverifyTokenWrapperverifyToken call chain and passes [ScopeType.DocRead, ScopeType.DocWrite] from the PATCH handler. This matches the scope enforcement already used by the adjacent POST /broadcast-signal endpoint via verifyStorageToken.

Two new tests confirm that:

  • A doc:read-only token is rejected with 403
  • A doc:write-only token (missing doc:read) is also rejected with 403

The existing "process normally" test with a full-scope token continues to pass, confirming no regression for legitimate callers.

The review process is outlined on this wiki
page
.

  • This is a security fix (MSRC 113235). The changes are intentionally minimal and surgical — 4 hunks in the production file, all adding a single requiredScopes parameter through the existing function chain.
  • verifyRequest and verifyTokenWrapper are module-private (not exported). The create() export signature is unchanged. Zero consumer impact.

)

The `PATCH /:tenantId/:id/root` endpoint in Alfred's REST API called
`verifyToken()` without passing the `requiredScopes` parameter. This
meant any valid JWT — including tokens with only `doc:read` scope —
could write arbitrary key-value data into a document's root map. The
injected operations were permanently sequenced into the document's op
log and replayed by all connected clients.

The fix threads a `requiredScopes` parameter through the `verifyRequest`
→ `verifyTokenWrapper` → `verifyToken` call chain and passes
`[ScopeType.DocRead, ScopeType.DocWrite]` from the PATCH handler. This
matches the scope enforcement already used by the adjacent `POST
/broadcast-signal` endpoint via `verifyStorageToken`.

Two new tests confirm that:
- A `doc:read`-only token is rejected with 403
- A `doc:write`-only token (missing `doc:read`) is also rejected with
403

The existing "process normally" test with a full-scope token continues
to pass, confirming no regression for legitimate callers.

The review process is outlined on [this wiki
page](https://github.com/microsoft/FluidFramework/wiki/PR-Guidelines#guidelines).

- This is a security fix (MSRC 113235). The changes are intentionally
minimal and surgical — 4 hunks in the production file, all adding a
single `requiredScopes` parameter through the existing function chain.
- `verifyRequest` and `verifyTokenWrapper` are module-private (not
exported). The `create()` export signature is unchanged. Zero consumer
impact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

Warning

WARNING: This PR is targeting a release branch!

All changes must first be merged into main and then backported to the target release branch.
Please include a link to the main PR in the description of this PR.

Changes to release branches require approval from the Patch Triage group before merging.
You should have already discussed this change with them so they know to expect it.

For more details, see our internal documentation for the patch policy and processes for
patch releases.

}
}

if (requiredScopes) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The PATCH /:tenantId/:id/root endpoint in Alfred's REST API had no scope enforcement — any valid JWT (even doc:read-only) could write to a document's root map.

PR #27073 added requiredScopes plumbing through verifyRequestverifyTokenWrapperverifyToken and passed [ScopeType.DocRead, ScopeType.DocWrite] from the PATCH handler. However, on the release/server/6.0 branch, verifyToken() in @fluidframework/server-services-utils didn't have a requiredScopes parameter — that was added on main by PR #24853. So the scopes were silently ignored and tests expecting 403 got 200.

The additional fix (in auth.ts) backports the minimal piece from #24853:

  • Adds requiredScopes?: string[] as an optional parameter to verifyToken()
  • Adds scope validation logic: checks that every required scope is present in the token's claims.scopes, throwing a 403 NetworkError if not

@sonalideshpandemsft sonalideshpandemsft marked this pull request as ready for review April 21, 2026 20:43
@sonalideshpandemsft sonalideshpandemsft requested review from a team and noencke April 21, 2026 20:43
@sonalideshpandemsft sonalideshpandemsft enabled auto-merge (squash) April 21, 2026 21:16
@sonalideshpandemsft sonalideshpandemsft merged commit 83e1fa9 into release/server/6.0 Apr 23, 2026
25 checks passed
@sonalideshpandemsft sonalideshpandemsft deleted the fix-scopes-6.0-1 branch April 23, 2026 14:54
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.

3 participants