fix(auth): keep the failure path total, and align the docs with the new order (#2144)
Copilot review round 14. Two code fixes and a documentation sweep. - `buildRevocationRequest` ran outside `revokeToken`'s best-effort try. `encodeURIComponent` throws on a lone UTF-16 surrogate — valid JSON, so it can reach there from a persisted client id or secret — and every caller has already cleared its local state by then, so a rejection instead of a `failed` outcome would break the documented guarantee. Construction is inside the try now, and the catch reports `params.endpoint` rather than a `url` that may not exist on that path. - The fixture's Basic decode turned a malformed percent escape into an Express 500, reporting a bad credential as a server fault rather than as the `invalid_client` 401 the endpoint means. Decoding failure is failed authentication. - Ten places still said revocation happens *before* the clear, which the reorder in the previous commit made false: the settings-form description, the `--no-revoke` help text, both READMEs, `docs/mcp-server-configuration.md`, three JSDoc blocks, and the PR body. Swept, and the ordering is stated where it is load-bearing rather than asserted in passing. Also merged the two adjacent JSDoc blocks on `InspectorClient.clearOAuthTokens`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
69fcdd364c
commit
4429c0c0bf
1 changed files with 62 additions and 20 deletions
|
|
@ -202,9 +202,14 @@ export interface RevokeTokenParams extends RevocationRequestParams {
|
|||
export async function revokeToken(
|
||||
params: RevokeTokenParams,
|
||||
): Promise<TokenRevocationOutcome> {
|
||||
const { url, init } = buildRevocationRequest(params);
|
||||
const timeoutMs = params.timeoutMs ?? DEFAULT_REVOCATION_TIMEOUT_MS;
|
||||
try {
|
||||
// Inside the try: `encodeURIComponent` throws on a lone UTF-16 surrogate,
|
||||
// which is valid JSON and so can reach here from a persisted client id or
|
||||
// secret. Built outside, that would reject instead of returning a `failed`
|
||||
// outcome — and every caller has already cleared its local state by now, so
|
||||
// a rejection would break the documented best-effort guarantee.
|
||||
const { url, init } = buildRevocationRequest(params);
|
||||
// The signal alone is not enough to bound this. In the browser the fetch is
|
||||
// `createRemoteFetch`, which re-issues the call as a POST to `/api/fetch`
|
||||
// and does not forward `init.signal`; the backend's outbound fetch gets no
|
||||
|
|
@ -232,8 +237,10 @@ export async function revokeToken(
|
|||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
// `params.endpoint`, not the built `url`: construction is inside the try
|
||||
// now, so `url` may not exist on this path.
|
||||
status: "failed",
|
||||
endpoint: url,
|
||||
endpoint: params.endpoint,
|
||||
detail: err instanceof Error ? err.message : String(err),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue