fix(client/auth): bind negative-check verdicts to an observed prerequisite (#483)

A negative check that reads only the final verdict scores SUCCESS whenever
the client fails to reach the requirement at all: "declined" and "never got
far enough to decide" are the same observation. auth/resource-mismatch
decided on !authorizationRequestMade alone, so a client that threw before
making a single HTTP request passed it.

Gate each verdict on the prerequisite the harness already records:
prm-pathbased-requested for the PRM resource comparison, and
authorization-server-metadata for the RFC 9207 iss comparisons, which
SEP-2468 conditions on the issuer recorded from the validated metadata
document. When the prerequisite is not observed the requirement was never
exercised, so report it via notTestable() (#248) rather than SUCCESS.

auth/metadata-issuer-mismatch already gated on the metadata fetch but
reported the unreached case as a plain violation; it now reports untestable
too. All six checks record propertyReached and stopReason in details.

Adds an inert client and a vitest case, so the checks are shown to catch
something and not merely to avoid false-positiving.
This commit is contained in:
Abhinav Singh 2026-09-07 00:44:00 +05:30 • committed by GitHub
commit 31c5e7aa55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,22 @@
#!/usr/bin/env node
import { runAsCli } from './helpers/cliRunner';
/**
* Broken client that gives up before performing any discovery request.
*
* BUG: it never fetches Protected Resource Metadata, so it never reads — let
* alone validates — the `resource` value the scenario mismatches on purpose.
*
* It exists to pin issue #467. `auth/resource-mismatch` decides its verdict
* from `!authorizationRequestMade` alone, and a client that does nothing at
* all satisfies that verdict, so the check scores SUCCESS for a client that
* cannot possibly have performed the validation under test.
*/
export async function runClient(_serverUrl: string): Promise<void> {
throw new Error(
'inert client: aborted before any discovery request (no PRM fetch, no authorization request)'
);
}
runAsCli(runClient, import.meta.url, 'auth-test-inert <server-url>');