Skip to content

Enumerate the /auth/** dynamic route family — 54 SDK methods rest on a prefix claim, not a route #3656

Description

@os-zhuang

Quantified by the capstone URL-conformance guard (#3642), which reports and ratchets exactly this weakness rather than hiding it.

The number

The guard matches each SDK-built URL against the union of the four ledgers, and separately counts how many matched only via a ** wildcard row. That count is 60 of ~196 matched calls — roughly a third of the SDK:

Wildcard row Methods it covers Namespaces
* /auth/** 54 auth (26), organizations (~19), oauth (6), a few others
* /ai/** 3 ai.nlq, ai.suggest, ai.insights
* /mcp/** 0 reached by the SDK sweep

/auth/** alone is 54. Those rows are honest — the routes really are built at runtime (better-auth's own table via plugin-auth; service-ai buildAIRoutes() at plugin start) — but a ** row asserts only that a prefix family is claimed, not that any specific URL in it resolves.

Why this matters concretely

auth.me and auth.refreshToken both build GET /api/v1/auth/get-session. The guard confirms the prefix is claimed; it cannot confirm better-auth actually exposes get-session under that name. Rename it upstream — better-auth is a third-party dependency on its own release cadence — and 26 auth.* methods start 404-ing with every guard in the repo still green. That is precisely the #3584 / #3611 / #3636 failure mode, just with an external trigger instead of an internal one, and it is concentrated on the most security-relevant namespace in the SDK.

The exposure is real rather than theoretical: this repo already had to chase better-auth 1.7 column drift in #3624 / #3647.

What would close it

plugin-auth mounts better-auth's handler; the route table is obtainable at runtime rather than by hand:

  1. Enumerate at start and ledger it — the same seam tranche 3 used for the autonomous service mounts: drive the plugin's registration against a capturing mock IHttpServer, diff the captured list against an auth-route-ledger.ts in plugin-auth. Whether better-auth exposes its table cleanly is the thing to check first; if it mounts one catch-all, this collapses to option 2.
  2. Pin the subset the SDK actually calls — ~30 concrete paths — and assert each resolves against a booted auth plugin. Narrower than a full ledger, but it covers exactly what would break, and an integration-style assertion catches an upstream rename that a static list cannot.

Option 2 is likely the better value here, because the risk is upstream drift and only a live check detects that.

Doing either lowers the ratchet in client-url-conformance.test.ts (currently toBeLessThanOrEqual(60)); the bound is written so it can only shrink.

Refs: #3642 (the guard and the ratchet), #3563 / #3587 / #3636 (the three ledger tranches), #3624 / #3647 (prior better-auth drift).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions