Skip to content

test(backend): route auth matrix over the app's route table - #305

Merged
arrzdev merged 1 commit into
mainfrom
test/route-auth-matrix
Nov 9, 2026
Merged

arrzdev merged 1 commit into
mainfrom
test/route-auth-matrix

Conversation

@arrzdev

@arrzdev arrzdev commented Nov 9, 2026

Copy link
Copy Markdown
Owner

What and why

Each route test checked its own auth by hand, so a new route without requireAuth would have passed CI. apps/backend/src/http/route-auth.test.ts now checks the whole API against the app's own route table (inspectRoutes from hono/dev, middleware left out), using real local D1:

  • Every route gets sorted. Each registered handler has to be in PUBLIC_ROUTES (with a reason) or SIGNED_IN_ROUTES. A route in neither list fails, and the failure shows its method and path. A listed route that no longer exists fails too.
  • Signed out means 401. Every signed-in route is called with no session and has to answer 401 unauthorized. A bogus bearer token gets 401 as well.
  • Other users get 404. A user who isn't a party gets 404 not_found on every game and invite route that takes an id: read game, read moves, play move, resign, decline, delete, and accept-invite through POST /games. Wrong-role calls are covered too: the sender declining their own invite, the recipient deleting it, the sender accepting it. Afterwards the game and invites are unchanged, the outsider's lists are empty, and /profile/me returns the caller's own profile. A meta-check fails if a signed-in route with a path parameter has no cross-user entry.

Gaps found: none. Every game and invite read and write already goes through a query scoped to the caller. To prove the test catches regressions, I made two temporary changes and reverted both: removing requireAuth from /realtime/ticket made it fail with expected 500 to be 401, and adding an unlisted GET /api/v1/secret made it fail with [ 'GET /api/v1/secret' ].

Out of the cross-user matrix, on purpose:

  • POST /invite-links/:id/accept: anyone holding the link can use it, by design.
  • Profile and avatar upload: they only act on /me and take no user id.
  • POST /events/game: anonymous counts, and nothing in it belongs to a user.
  • Dev avatar server: not found in production.
  • Guest sessions: the server has none. A guest is a signed-out caller, which the 401 check covers.

How to verify

pnpm --filter @repo/backend exec vitest run src/http/route-auth.test.ts

Sort every handler the app registers into public or signed-in, call each
signed-in route signed out and expect 401, and check that another user gets
404 on every game and invite route that takes an id.
@arrzdev
arrzdev force-pushed the test/route-auth-matrix branch from 192c6a1 to 52f8307 Compare November 9, 2026 10:34

@arrzdev arrzdev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Approve. The route table comes from hono's inspector, so an unlisted route fails, and so does a stale entry. Signed-out 401s are checked on every signed-in route, and the meta-check makes every id route carry a cross-user case. The wrong-role invite cases (sender declining, recipient deleting, sender accepting) are a good addition. Main has moved 2 commits since this branch, but neither touches a route, so the matrix still holds.

@arrzdev
arrzdev merged commit ba895fd into main Nov 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant