Skip to content

Commit 0caa55a

Browse files
committed
fix(webapp): mask 404 as 403 when findResource returns null on authorized routes
createActionApiRoute now runs findResource before authorization so the auth scope check can expand to alternate identifiers of the resolved resource (Sessions are addressable by both friendlyId and externalId). Side-effect: an authenticated-but-underscoped caller could probe resource existence by observing 404 vs 403. Mask the 404 as 403 with the same response shape as the auth-failed branch when the route declares authorization, so the two cases are indistinguishable to callers without scopes. Routes without authorization keep returning 404.
1 parent 1a880fd commit 0caa55a

1 file changed

Lines changed: 20 additions & 0 deletions

File tree

apps/webapp/app/services/routeBuilders/apiBuilder.server.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,26 @@ export function createActionApiRoute<
683683
: undefined;
684684

685685
if (options.findResource && !resource) {
686+
// When the route also declares `authorization`, mask "resource
687+
// doesn't exist" as 403 — same shape as the auth-failed branch
688+
// below — so an authenticated-but-underscoped caller can't
689+
// probe resource existence by observing 404 vs 403. Routes
690+
// without an `authorization` block keep returning 404.
691+
if (authorization) {
692+
return await wrapResponse(
693+
request,
694+
json(
695+
{
696+
error: `Unauthorized: missing required scopes`,
697+
code: "unauthorized",
698+
param: "access_token",
699+
type: "authorization",
700+
},
701+
{ status: 403 }
702+
),
703+
corsStrategy !== "none"
704+
);
705+
}
686706
return await wrapResponse(
687707
request,
688708
json({ error: "Resource not found" }, { status: 404 }),

0 commit comments

Comments
 (0)