Skip to content

fix(auth): report the status when a request is rejected without a body - #43

Open
ExtraToast wants to merge 1 commit into
mainfrom
fix/report-bodyless-rejection-status
Open

ExtraToast wants to merge 1 commit into
mainfrom
fix/report-bodyless-rejection-status

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Summary

A request rejected by a servlet filter carries no ProblemDetail body. unwrap read error === undefined, fell through to data == null and threw Error('Missing response body'); the store's isProblemDetail check then failed and the form showed "Login failed" — a message that names neither the status nor the fact that the server refused the request before reading it.

That message cost a full investigation. A sign-in failure reported as a 401 turned out to be /api/oauth2/authorize answering 401 to a non-HTML Accept (JorisJonkers-dev/auth-api#59), and the session-login attempts that followed were 400 invalid-credentials — wrong credentials, which the form also reports as "Login failed" whenever the body cannot be read. The status is the one fact that separates "your password is wrong" from "a filter refused this", and the SPA discarded it.

Fix

ClientResult now carries the Response. isRejected treats a non-ok response as a rejection even when the client reports no error payload, and rejectionOf synthesizes a ProblemDetail from the status when there is no body to read:

The server rejected the request with status 401.

An error response that does carry a ProblemDetail is passed through untouched, so every existing message — including the 400 invalid-credentials detail the API does return — is unchanged.

Verification

  • New test in authService.test.ts drives a bodyless 401 through sessionLogin and asserts the thrown value carries status and the status-bearing detail. Proven to fail before the fix by reverting the isRejected response check — it threw Error('Missing response body').
  • pnpm test124 tests passed across 13 files, on main at release 0.4.4.
  • pnpm lint (--max-warnings 0) and pnpm typecheck — clean.

Risk

  • Low, and confined to the failure path: a successful response never reaches either new function, and an error carrying a body keeps its own detail.
  • The synthesized detail names a status rather than a cause, which is deliberate — it is the only thing known about a rejection that never reached a controller.

Links

A request rejected by a servlet filter carries no ProblemDetail body, so
unwrap threw Error('Missing response body') and the form fell back to a
generic "Login failed" — hiding a 401 from the resource-server filter.

A non-ok response is now treated as a rejection even with no error
payload, and the status is reported in a synthesized ProblemDetail.
@ExtraToast ExtraToast added type: bug Something is broken or behaving incorrectly. area: auth Authentication, authorization, sessions, or identity. component: ui User interface component or frontend application. labels Sep 19, 2026
@ExtraToast ExtraToast self-assigned this Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Authentication, authorization, sessions, or identity. component: ui User interface component or frontend application. type: bug Something is broken or behaving incorrectly.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant