fix(auth): 401 (not 403) for anonymous callers so the SPA redirects to login#605
Closed
remyluslosius wants to merge 2 commits into
Closed
fix(auth): 401 (not 403) for anonymous callers so the SPA redirects to login#605remyluslosius wants to merge 2 commits into
remyluslosius wants to merge 2 commits into
Conversation
An anonymous request (no credentials, or a session cookie that expired in the browser and is no longer sent) to a protected endpoint now returns 401 auth.required instead of 403. The SPA redirects to login on a 401, so an expired session surfaces as a clean re-login prompt rather than a dead-end 'failed to load'. An authenticated caller whose role lacks the permission still gets 403 authz.permission_denied; the audit event is unchanged for both.
The 12 specs/tests that strictly asserted anonymous -> 403 now assert 401 auth.required (alerts, audit-events-query, fleet-observability, host-system-info, os-intelligence, system-rbac AC-09/AC-15, system/fleet connectivity, discovery/ intelligence config). Authenticated-but-unauthorized -> 403 language preserved. Specs that already said '401/403' are unchanged.
This was referenced Jun 19, 2026
Contributor
Author
|
Folded into #609 (release: bundle 0.2.0-rc.11) and merged there to avoid the CHANGELOG rebase cascade. Content is on main. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Return 401 (not 403) for anonymous callers on protected endpoints
Why
An expired session surfaced as a dead-end "Failed to load" instead of a login redirect. Root cause: when a session cookie expires in the browser it stops being sent, so the request arrives with no credentials, is bound anonymous, and the RBAC layer returned 403. The SPA redirects to login on a 401, but treats a 403 as "you lack permission" — so it showed a data-load error. (The identity binder already 401s a presented-but-rejected session; this closes the no-credentials gap.)
Change
internal/auth/middleware.go(denyPermission): an anonymous caller (IsAnonymous) now gets 401auth.requiredwith aWWW-Authenticateheader; an authenticated caller whose role lacks the permission still gets 403authz.permission_denied. Theauthz.permission.deniedaudit event is emitted in both cases (unchanged).auth.requirederror code (api/error_codes.yaml).client.tsalready recognizes a 401 withauth.required(inAUTH_FAILURE_CODES) and fail-closes any 401 to a login redirect.Contract sweep
The 12 specs/tests that strictly asserted anonymous→403 now assert 401
auth.required(alerts, audit-events-query, fleet-observability, host-system-info, os-intelligence,system-rbacAC-09/AC-15, system/fleet connectivity, discovery/intelligence config). The ~7 specs that already said "401/403" are unchanged. Authenticated-but-unauthorized→403 language preserved throughout.Verified locally
internal/auth+internal/serversuite: green (exit 0).Docs/behavior note: this is the first of the two UX items from the session — the 401 redirect. (The second, treating 401 as re-auth rather than surfacing the data error, is already handled by the existing frontend interceptor.)