Skip to content

fix(meshcore): stop a spurious "Authentication required" banner for read-only viewers - #4489

Closed
Yeraze wants to merge 2 commits into
mainfrom
fix/meshcore-anon-auth-banner
Closed

fix(meshcore): stop a spurious "Authentication required" banner for read-only viewers#4489
Yeraze wants to merge 2 commits into
mainfrom
fix/meshcore-anon-auth-banner

Conversation

@Yeraze

@Yeraze Yeraze commented Aug 1, 2026

Copy link
Copy Markdown
Owner

An anonymous viewer with read grants on a MeshCore source got "Authentication required" splashed over the channel view — which reads as "you can't view this channel" even though the page was otherwise working.

Reproduced in an anonymous session

Four calls failed; two of them were the culprit:

call status
config/default-scope 401 ← the banner
saved-regions 401 ← the banner
messages/channel-counts 403 separate — see below
messages/channel/0 403 separate — see below

Both 401 routes are requireAuth() + configuration: read, and both exist only to populate the scope-override datalist — a send-side control a read-only viewer cannot use. fetchSavedRegions then surfaced the raw 401 body through setError, so the server's {"error":"Authentication required"} became a page-level banner.

Fix

Two changes, either of which resolves the symptom; both are correct:

  • Gate both lookups on canSend — the requests are never made for a viewer who cannot send, rather than made and ignored.
  • Drop the setError from fetchSavedRegions, matching getDefaultScope immediately above it. Optional autocomplete suggestions must not be able to raise a page-level error.

Permissions UI

connection moves to the top of the RESOURCES list, reworded to "Required to open a source. Also controls connect/disconnect."

MeshCoreSourcePage gates its entire surface on connection: read, so without it every other grant on a MeshCore source is inert and the operator only sees "You do not have permission to view this MeshCore source" — with no hint which permission is missing. This cost a real user a debugging session.

Worth noting for reviewers: that gate is MeshCore-only. No Meshtastic path checks connection: read (verified by grep), despite the comment there claiming it behaves "just like a Meshtastic source the user can't read".

Deliberately NOT changed

Channel content still requires messages: read. That isn't a bug — the server says so explicitly in its 403 body:

{"error":"Insufficient permissions","code":"FORBIDDEN",
 "required":{"resource":"messages","action":"read"}}

Also worth flagging as a separate UX wart, not fixed here: channel_0..7 are offered on MeshCore sources but are inert — no MeshCore code path consults channel_N (verified by grep). A user granting channel_0 on a MeshCore source reasonably expects channel access and gets nothing. Hiding those for MeshCore sources would be its own change.

Test plan

  • 2 new tests in MeshCoreChannelsView.test.tsx — the auth-only lookups are not called without messages: write, and still are for a viewer who can send.
  • Confirmed a real regression test: reverting the component change fails the first one.
  • Full Vitest suite — success: true, 12956 passed, 0 failed.
  • npx tsc --noEmit clean; npm run lint:ci no FAIL lines.
  • Verified anonymously on the running container: banner gone, and neither auth-only endpoint is requested at all.

Generated by Claude Code

Yeraze and others added 2 commits August 1, 2026 12:09
… nothing

Two independent pre-existing bugs on the MeshCore channel view, both dating
to the per-channel history introduced in #4461. Reported from the running
app; neither file was touched by the #4473 nav work.

Delete did nothing visible. The stream renders `filtered` =
merge(history, messages), but `actions.deleteMessage` only prunes the shared
`messages` pool. Anything served from the per-channel backlog — nearly
everything on this view — was deleted server-side and then immediately
re-rendered from `history`. Confirmed against the live API: the row was
already gone from the database while still on screen. `handleDeleteMessage`
and `handleClearChannel` now prune `history` (and the count badge) on
success, and only on success: a failed delete leaves the row rather than
vanishing optimistically.

The channel opened at the top instead of the newest message — mobile only,
which is why desktop testing missed it. The mobile two-pane layout mounts
the conversation but hides it until the operator drills in from the list;
measured in that state, scrollHeight 0 / clientHeight 0 / offsetParent null.
The entry scroll fired there, `scrollTop = scrollHeight` was `0 = 0` — a
silent no-op — but the one shot was already spent, so drilling in left the
view pinned at the top of a 17,000px backlog with nothing left to
re-trigger it. The scroll no longer spends the shot until the list has real
layout, and a ResizeObserver completes it the moment the pane is revealed.
This fixes the DM view too, which shares the component.

The guard keys off scrollHeight alone, NOT clientHeight: a hidden subtree
zeroes both, but jsdom reports clientHeight 0 for everything, so a
clientHeight guard would silently disable the entry scroll in every test.

Verified on the running container at 390x780 across both MeshCore sources:
entry scroll lands at distance 0 from the bottom (was 16774), and deleting
removes the row immediately with the server confirming it gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9NzRtqE8eSMS8tvAeodUB
…ead-only viewers

An anonymous viewer with read grants on a MeshCore source got
"Authentication required" splashed over the channel view, which reads as
"you can't view this channel" even though the page was otherwise working.

Reproduced in an anonymous browser session. Two of the four failing calls
were 401s:

  config/default-scope   401
  saved-regions          401

Both are `requireAuth()` + `configuration: read`, and both exist ONLY to
populate the scope-override datalist — a send-side control a read-only
viewer cannot use. `fetchSavedRegions` then surfaced the raw 401 body via
setError, so the server's "Authentication required" became a page banner.

Two changes, either of which would fix the symptom; both are correct:
  - Gate both lookups on `canSend`, so the requests are never made for a
    viewer who cannot send rather than made and ignored.
  - Drop the setError from fetchSavedRegions, matching getDefaultScope
    directly above it. Optional autocomplete suggestions must not be able to
    raise a page-level error.

Also moves `connection` to the top of the RESOURCES list and rewords it to
"Required to open a source." MeshCoreSourcePage gates its ENTIRE surface on
connection:read, so without it every other grant on a MeshCore source is
inert and the operator only sees "You do not have permission to view this
MeshCore source" — with no hint which permission is missing. Note the gate is
MeshCore-only; no Meshtastic path checks it, despite the comment there
claiming parity.

Verified anonymously on the running container: banner gone, and neither
auth-only endpoint is requested at all.

Not changed: channel CONTENT still needs `messages: read`, which the server
states explicitly in its 403 body
(required: {resource: "messages", action: "read"}). `channel_0..7` are inert
on MeshCore sources — no MeshCore code path consults them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9NzRtqE8eSMS8tvAeodUB
@Yeraze

Yeraze commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #4491, which carries this commit plus the channel-sync guard and the reply/trigger ordering fix.

This branch was stacked on the pre-squash version of #4488, so once that merged it went CONFLICTING against main. Rather than rebase it separately, the commit was cherry-picked onto the combined branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant